Merge branch 'develop'

This commit is contained in:
ISA
2024-11-28 15:07:02 +01:00
243 changed files with 15697 additions and 4208 deletions

View File

@@ -5,58 +5,75 @@ 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";
import EditModeToggle from "./EditModeToggle";
import { polylineLayerVisibleState } from "../store/atoms/polylineLayerVisibleState"; // Import für Polyline-Visibility
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);
const [polylineVisible, setPolylineVisible] = useRecoilState(polylineLayerVisibleState); // Zustand für Polylines
useEffect(() => {
const storedPoiVisible = localStorage.getItem("poiVisible");
if (storedPoiVisible !== null) {
setPoiVisible(storedPoiVisible === "true");
}
const storedPolylineVisible = localStorage.getItem("polylineVisible");
if (storedPolylineVisible !== null) {
setPolylineVisible(storedPolylineVisible === "true");
}
const storedMapLayersVisibility = localStorage.getItem("mapLayersVisibility");
if (storedMapLayersVisibility) {
setMapLayersVisibility(JSON.parse(storedMapLayersVisibility));
}
}, [setPoiVisible, setMapLayersVisibility]);
const handleAreaChange = (event) => {
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
)
);
const allowedSystems = new Set(GisSystemStatic.filter((system) => system.Allow === 1).map((system) => system.IdSystem));
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;
});
console.log("filterdArea GisStationsStaticDistrict:", filteredAreas);
console.log("GisSystemStatic:", GisSystemStatic);
console.log("allowedSystems:", allowedSystems);
setStationListing(
filteredAreas.map((area, index) => ({
id: index + 1,
name: area.Area_Name,
}))
);
const seenSystemNames = new Set();
const filteredSystems = GisSystemStatic.filter((item) => {
const formattedName = item.Name.replace(/[\s\-]+/g, "");
console.log(formattedName);
const isUnique = !seenSystemNames.has(formattedName) && item.Allow === 1;
if (isUnique) {
seenSystemNames.add(formattedName);
}
return isUnique;
});
setSystemListing(
filteredSystems.map((system, index) => ({
id: index + 1,
@@ -67,35 +84,38 @@ function DataSheet() {
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);
localStorage.setItem("mapLayersVisibility", JSON.stringify(newState)); // Store in localStorage
return newState;
});
};
const handlePoiCheckboxChange = (event) => {
const { checked } = event.target;
setPoiVisible(checked);
localStorage.setItem("poiVisible", checked); // Store POI visibility in localStorage
};
const handleIconClick = () => {
setSelectedArea("Station wählen");
setZoomTrigger((current) => current + 1);
};
const handlePolylineCheckboxChange = (event) => {
const { checked } = event.target;
setPolylineVisible(checked);
localStorage.setItem("polylineVisible", checked); // Store Polyline visibility in localStorage
};
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] max-w-[400px] 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"
>
<div className="flex items-center justify-between space-x-2">
<select onChange={handleAreaChange} id="stationListing" className="border-solid-1 p-2 rounded ml-1 font-semibold" style={{ minWidth: "150px", maxWidth: "200px" }}>
<option value="Station wählen">Station wählen</option>
{stationListing.map((station) => (
<option key={station.id} value={station.id}>
@@ -103,37 +123,38 @@ function DataSheet() {
</option>
))}
</select>
<img
src="/img/expand-icon.svg"
alt="Expand"
className="h-6 w-6 ml-2 cursor-pointer"
onClick={handleIconClick}
/>
<div className="flex items-center space-x-2">
<EditModeToggle />
<img src="/img/expand-icon.svg" alt="Expand" className="h-6 w-6 cursor-pointer" onClick={handleIconClick} />
</div>
</div>
<div>
{/* Checkboxen in einem gemeinsamen Container */}
<div className="flex flex-col gap-2">
{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>
<br />
<div className="flex items-center">
<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>
</div>
</React.Fragment>
))}
<input
type="checkbox"
checked={poiVisible}
onChange={(e) => {
const checked = e.target.checked;
setPoiVisible(checked);
console.log(
`POIs sind jetzt ${checked ? "sichtbar" : "nicht sichtbar"}.`
);
}}
/>
<label className="text-sm ml-2">POIs</label>
<div className="flex items-center">
<input type="checkbox" checked={poiVisible} onChange={handlePoiCheckboxChange} id="poi-checkbox" />
<label htmlFor="poi-checkbox" className="text-sm ml-2">
POIs
</label>
</div>
<div className="flex items-center">
<input type="checkbox" checked={polylineVisible} onChange={handlePolylineCheckboxChange} id="polyline-checkbox" />
<label htmlFor="polyline-checkbox" className="text-sm ml-2">
Kabelstrecken
</label>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,38 @@
// /components/EditModeToggle.js
import React, { useState, useEffect } from "react";
import EditOffIcon from "@mui/icons-material/EditOff";
import ModeEditIcon from "@mui/icons-material/ModeEdit";
import Tooltip from "@mui/material/Tooltip"; // Importiere Tooltip von Material-UI
function EditModeToggle() {
const [editMode, setEditMode] = useState(() => localStorage.getItem("editMode") === "true");
const toggleEditMode = () => {
const newEditMode = !editMode;
setEditMode(newEditMode);
localStorage.setItem("editMode", newEditMode);
//Browser neu laden, um die Änderungen anzuwenden
window.location.reload();
};
useEffect(() => {
const storedMode = localStorage.getItem("editMode") === "true";
setEditMode(storedMode);
}, []);
return (
<div onClick={toggleEditMode} style={{ cursor: "pointer" }}>
{editMode ? (
<Tooltip title="Bearbeitungsmodus deaktivieren" placement="top">
<EditOffIcon />
</Tooltip>
) : (
<Tooltip title="Bearbeitungsmodus aktivieren" placement="top">
<ModeEditIcon />
</Tooltip>
)}
</div>
);
}
export default EditModeToggle;

File diff suppressed because it is too large Load Diff

View File

@@ -16,9 +16,7 @@ const PoiUpdateModal = ({ onClose, poiData }) => {
const [deviceName, setDeviceName] = useState("");
const [idLD, setIdLD] = useState(poiData ? poiData.idLD : "");
const [description, setDescription] = useState(
poiData ? poiData.description : ""
);
const [description, setDescription] = useState(poiData ? poiData.description : "");
useEffect(() => {
if (poiData) {
@@ -37,16 +35,11 @@ const PoiUpdateModal = ({ onClose, poiData }) => {
const fetchDeviceId = async () => {
if (poiData && poiData.idLD) {
try {
const response = await fetch(
`/api/talas_v5_DB/locationDevice/getDeviceIdById?idLD=${poiData.idLD}`
);
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
);
console.error("Fehler beim Abrufen der Geräteinformation in PoiUpdateModel.js: ", error);
}
}
};
@@ -57,12 +50,9 @@ const PoiUpdateModal = ({ onClose, 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",
}
);
const response = await fetch(`/api/talas_v5_DB/pois/deletePoi?id=${poiId}`, {
method: "DELETE",
});
if (response.ok) {
alert("POI wurde erfolgreich gelöscht.");
onClose();
@@ -71,7 +61,7 @@ const PoiUpdateModal = ({ onClose, poiData }) => {
throw new Error("Fehler beim Löschen des POI.");
}
} catch (error) {
console.error("Fehler beim Löschen des POI:", error);
console.error("Fehler beim Löschen des POI 1:", error);
alert("Fehler beim Löschen des POI.");
}
}
@@ -106,16 +96,13 @@ const PoiUpdateModal = ({ onClose, poiData }) => {
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);
const selectedDevice = data.find((device) => device.id === poiData.idLD);
setDeviceName(selectedDevice ? selectedDevice.id : data[0].id); // Hier wird die ID als initialer Zustand gesetzt
console.log("Selected Device:", selectedDevice);
console.log("Selected devciceName:", deviceName);
}
} catch (error) {
console.error(
"Fehler beim Abrufen der Standort- und Gerätedaten:",
error
);
console.error("Fehler beim Abrufen der Standort- und Gerätedaten:", error);
}
};
fetchData();
@@ -126,9 +113,10 @@ const PoiUpdateModal = ({ onClose, poiData }) => {
.then((response) => response.json())
.then((data) => {
setLocationDeviceData(data);
const currentDevice = data.find(
(device) => device.idLD === currentPoi.idLD
);
console.log("Standort- und Gerätedaten 3:", data);
console.log("Standort- und Gerätedaten 3 poiData:", poiData);
// Findet das Gerät, das der aktuellen IDLD entspricht
const currentDevice = data.find((device) => device.idLD === currentPoi.idLD);
if (currentDevice) {
setDeviceName(currentDevice.name);
}
@@ -141,9 +129,7 @@ const PoiUpdateModal = ({ onClose, poiData }) => {
const handleSubmit = async (event) => {
event.preventDefault();
const idLDResponse = await fetch(
`/api/talas_v5_DB/locationDevice/getDeviceId?deviceName=${encodeURIComponent(deviceName)}`
);
const idLDResponse = await fetch(`/api/talas_v5_DB/locationDevice/getDeviceId?deviceName=${encodeURIComponent(deviceName)}`);
const idLDData = await idLDResponse.json();
const idLD = idLDData.idLD;
try {
@@ -166,9 +152,7 @@ const PoiUpdateModal = ({ onClose, poiData }) => {
window.location.reload();
} else {
const errorResponse = await response.json();
throw new Error(
errorResponse.error || "Fehler beim Aktualisieren des POI."
);
throw new Error(errorResponse.error || "Fehler beim Aktualisieren des POI.");
}
} catch (error) {
console.error("Fehler beim Aktualisieren des POI:", error);
@@ -176,39 +160,37 @@ const PoiUpdateModal = ({ onClose, poiData }) => {
}
};
//ausgewählte poi Informationen in Console anzeigen
console.log("Selected POI:", selectedPoi);
console.log("Selected POI Gerät id in poiUpdateModal.js:", selectedPoi.id);
console.log("Selected POI Typ name in poiUpdateModal.js:", selectedPoi.typ); //als Typ in dropdown menu
console.log("Selected POI Beschreibung in poiUpdateModal.js:", selectedPoi.description);
console.log("Selected POI Gerät deviceId in poiUpdateModal.js:", selectedPoi.deviceId);
return (
<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"
/>
<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 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) => (
console.log("device.id und name:", device),
(
<option key={index} value={device.id}>
{device.name}
</option>
)
)
)}
</select>
</div>
@@ -216,13 +198,7 @@ const PoiUpdateModal = ({ onClose, poiData }) => {
<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"
>
<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}
@@ -239,10 +215,7 @@ const PoiUpdateModal = ({ onClose, poiData }) => {
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"
>
<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>

View File

@@ -23,6 +23,7 @@ export const addMarkersToMap = (markers, map, layerGroup) => {
marker.on("mouseover", () => marker.openPopup());
marker.on("mouseout", () => marker.closePopup());
marker.on("dragend", (e) => {
console.log("Marker wurde verschoben in addMarkersToMap");
const newLat = e.target.getLatLng().lat;
const newLng = e.target.getLatLng().lng;
const markerId = e.target.options.id;
@@ -32,11 +33,7 @@ export const addMarkersToMap = (markers, map, layerGroup) => {
};
// Funktion zum Aktualisieren der Standorte in der Datenbank
export const updateLocationInDatabase = async (
id,
newLatitude,
newLongitude
) => {
export const updateLocationInDatabase = async (id, newLatitude, newLongitude) => {
const response = await fetch("/api/talas_v5_DB/pois/updateLocation", {
method: "POST",
headers: { "Content-Type": "application/json" },

37
components/TestScript.js Normal file
View File

@@ -0,0 +1,37 @@
// components/TestScript.js
import { useEffect } from "react";
import setupPolylinesCode from "!!raw-loader!../utils/setupPolylines.js"; // Lädt die gesamte setupPolylines.js als Text
export default function TestScript() {
useEffect(() => {
// Regulärer Ausdruck für "Stützpunkt entfernen" im Kontextmenü
const removeRegex = /marker\.on\("mouseover", function \(\) {\s*this\.bindContextMenu\({\s*contextmenuItems: \[\s*\{\s*text: "Stützpunkt entfernen"/;
// Regulärer Ausdruck für "Stützpunkt hinzufügen" im Kontextmenü
const addRegex = /contextmenuItems: \[\s*\{\s*text: "Stützpunkt hinzufügen"/;
// Stilvorlagen für das Konsolen-Logging
const successStyle = "color: #fff; background-color: #28a745; padding: 4px 8px; font-size: 14px; border-radius: 4px;";
const failStyle = "color: #fff; background-color: #dc3545; padding: 4px 8px; font-size: 14px; border-radius: 4px;";
const neutralStyle = "color: #006400; font-size: 14px; background-color: #f0f0f0; padding: 4px 8px; border-radius: 4px;";
// Überprüfung für "Stützpunkt entfernen"
if (removeRegex.test(setupPolylinesCode)) {
console.log("%c✔ Test bestanden: Der Text für 'Stützpunkt entfernen' wurde gefunden.", successStyle);
} else {
console.log("%c✘ Test fehlgeschlagen: Der Text für 'Stützpunkt entfernen' wurde nicht gefunden.", failStyle);
}
// Überprüfung für "Stützpunkt hinzufügen"
if (addRegex.test(setupPolylinesCode)) {
console.log("%c✔ Test bestanden: Der Text für 'Stützpunkt hinzufügen' wurde gefunden.", successStyle);
} else {
//console.log("%c✘ Test fehlgeschlagen: Der Text für 'Stützpunkt hinzufügen' wurde nicht gefunden.", failStyle);
}
// Beispiel einer neutralen Nachricht (falls benötigt)
console.log("%c Info: Überprüfung abgeschlossen.", neutralStyle);
}, []);
return null; // Keine visuelle Ausgabe erforderlich
}

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,26 @@
// /components/gisPolylines/PolylineContextMenu.js
import React from "react";
const PolylineContextMenu = ({ position, onAddPoint, onRemovePoint, onClose }) => {
return (
<div
style={{
position: "absolute",
top: position.y,
left: position.x,
backgroundColor: "white",
border: "1px solid black",
padding: "10px",
zIndex: 1000,
}}
>
<ul>
<li onClick={onAddPoint}>Stützpunkt hinzufügen</li>
<li onClick={onRemovePoint}>Stützpunkt entfernen</li>
<li onClick={onClose}>Schließen</li>
</ul>
</div>
);
};
export default PolylineContextMenu;

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],
});

162
components/imports.js Normal file
View File

@@ -0,0 +1,162 @@
// 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 { insertNewPOI, removePOI, handleEditPoi } from "../utils/poiUtils.js";
import { createAndSetDevices } from "../utils/createAndSetDevices.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/addContextMenuToMarker.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/initializeMap.js";
import { addItemsToMapContextMenu } from "./useMapContextMenu.js";
import useGmaMarkersLayer from "../hooks/layers/useGmaMarkersLayer.js"; // Import the custom hook
import useTalasMarkersLayer from "../hooks/layers/useTalasMarkersLayer.js"; // Import the custom hook
import useEciMarkersLayer from "../hooks/layers/useEciMarkersLayer.js";
import useGsmModemMarkersLayer from "../hooks/layers/useGsmModemMarkersLayer.js";
import useCiscoRouterMarkersLayer from "../hooks/layers/useCiscoRouterMarkersLayer.js";
import useWagoMarkersLayer from "../hooks/layers/useWagoMarkersLayer.js";
import useSiemensMarkersLayer from "../hooks/layers/useSiemensMarkersLayer.js";
import useOtdrMarkersLayer from "../hooks/layers/useOtdrMarkersLayer.js";
import useWdmMarkersLayer from "../hooks/layers/useWdmMarkersLayer.js";
import useMessstellenMarkersLayer from "../hooks/layers/useMessstellenMarkersLayer.js";
import useTalasiclMarkersLayer from "../hooks/layers/useTalasiclMarkersLayer.js";
import useDauzMarkersLayer from "../hooks/layers/useDauzMarkersLayer.js";
import useSmsfunkmodemMarkersLayer from "../hooks/layers/useSmsfunkmodemMarkersLayer.js";
import useUlafMarkersLayer from "../hooks/layers/useUlafMarkersLayer.js";
import useSonstigeMarkersLayer from "../hooks/layers/useSonstigeMarkersLayer.js";
import handlePoiSelect from "../utils/handlePoiSelect.js";
import { fetchGisStationsStaticDistrict, fetchGisStationsStatusDistrict, fetchGisStationsMeasurements, fetchGisSystemStatic } from "../services/fetchData.js";
import { setupPolylines } from "../utils/setupPolylines.js";
import { setupPOIs } from "../utils/setupPOIs.js";
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,
insertNewPOI,
removePOI,
createAndSetDevices,
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,
setupPOIs,
VersionInfoModal,
PoiUpdateModalWrapper,
AddPoiModalWindowWrapper,
useFetchPoiData,
usePoiTypData,
useMarkerLayers,
useLayerVisibility,
useLineData,
};

View File

@@ -0,0 +1,217 @@
// components/pois/AddPoiModalWindow.js
import React, { useState, useEffect } from "react";
import Select from "react-select"; // Importiere react-select
import { useSetRecoilState, useRecoilState } from "recoil";
import { mapLayersState } from "../../store/atoms/mapLayersState";
import { poiReadFromDbTriggerAtom } from "../../store/atoms/poiReadFromDbTriggerAtom";
const AddPoiModalWindow = ({ onClose, map, latlng }) => {
const [poiTypData, setpoiTypData] = useState([]);
const [name, setName] = useState("");
const [poiTypeId, setPoiTypeId] = useState(null); // Verwende null für react-select
const [latitude] = useState(latlng.lat.toFixed(5));
const [longitude] = useState(latlng.lng.toFixed(5));
const setTrigger = useSetRecoilState(poiReadFromDbTriggerAtom); // Verwende useSetRecoilState
const [locationDeviceData, setLocationDeviceData] = useState([]);
const [filteredDevices, setFilteredDevices] = useState([]); // Gefilterte Geräte
const [deviceName, setDeviceName] = useState(null); // Verwende null für react-select
const [mapLayersVisibility] = useRecoilState(mapLayersState); // Um die aktiven Layer zu erhalten
// Map von Systemnamen zu ids (wie zuvor)
const systemNameToIdMap = {
TALAS: 1,
ECI: 2,
ULAF: 3,
GSMModem: 5,
CiscoRouter: 6,
WAGO: 7,
Siemens: 8,
OTDR: 9,
WDM: 10,
GMA: 11,
Messdatensammler: 12,
Messstellen: 13,
TALASICL: 100,
DAUZ: 110,
SMSFunkmodem: 111,
Basisgerät: 200,
};
// API-Abfrage, um die Geräte zu laden
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);
const locationDeviceData = await locationDeviceResponse.json();
console.log("Geräte von der API:", locationDeviceData); // Geräte-Daten aus der API anzeigen
setLocationDeviceData(locationDeviceData);
// Filtere die Geräte basierend auf den sichtbaren Systemen
filterDevices(locationDeviceData);
} catch (error) {
console.error("Fehler beim Abrufen der Daten:", error);
}
};
fetchInitialData();
}, []);
// Funktion zum Filtern der Geräte basierend auf den aktiven Systemen (Layern)
const filterDevices = (devices) => {
const activeSystems = Object.keys(mapLayersVisibility).filter((system) => mapLayersVisibility[system]);
console.log("Aktive Systeme:", activeSystems); // Anzeigen der aktiven Systeme
// Mappe aktive Systeme auf ihre ids
const activeSystemIds = activeSystems.map((system) => systemNameToIdMap[system]).filter((id) => id !== undefined);
console.log("Aktive System-IDs:", activeSystemIds); // Anzeigen der aktiven System-IDs
// Filtere die Geräte nach aktiven Systemen basierend auf idsystem_typ
const filtered = devices.filter((device) => activeSystemIds.includes(device.idsystem_typ));
console.log("Gefilterte Geräte:", filtered); // Gefilterte Geräte anzeigen
setFilteredDevices(filtered); // Setze die gefilterten Geräte
};
// Wenn mapLayersVisibility sich ändert, filtere die Geräte erneut
useEffect(() => {
if (locationDeviceData.length > 0) {
filterDevices(locationDeviceData);
}
}, [mapLayersVisibility, locationDeviceData]);
const handleSubmit = async (event) => {
event.preventDefault();
if (!poiTypeId) {
alert("Bitte wählen Sie einen Typ aus.");
return;
}
const formData = {
name,
poiTypeId: poiTypeId.value,
latitude,
longitude,
idLD: filteredDevices.find((device) => device.name === deviceName?.value).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) => trigger + 1); // Verwenden des Triggers zur Aktualisierung
onClose();
window.location.reload();
} else {
console.error("Fehler beim Hinzufügen des POI");
}
if (map && typeof map.closePopup === "function") {
map.closePopup();
}
};
// Erstelle Optionen für react-select
const poiTypeOptions = poiTypData.map((poiTyp) => ({
value: poiTyp.idPoiTyp,
label: poiTyp.name,
}));
const deviceOptions = filteredDevices.map((device) => ({
value: device.name,
label: device.name,
}));
// Custom styles for react-select
const customStyles = {
control: (provided) => ({
...provided,
width: "100%",
minWidth: "300px", // Minimum width for the dropdown
maxWidth: "100%", // Maximum width (you can adjust this if needed)
}),
menu: (provided) => ({
...provided,
width: "100%",
minWidth: "300px", // Ensure the dropdown menu stays at the minimum width
}),
};
// Style für größere Breite des Modals und für Inputs
const modalStyles = {
// width: "300px", // größere Breite für das Modal
//maxWidth: "100%", // responsive, passt sich an
//padding: "20px", // Polsterung für das Modal
//backgroundColor: "white", // Hintergrundfarbe
//borderRadius: "8px", // Abgerundete Ecken
//boxShadow: "0px 4px 12px rgba(0, 0, 0, 0.1)", // Schatten für das Modal
};
return (
<form onSubmit={handleSubmit} style={modalStyles} className="m-0 p-2 w-full">
<div className="flex flex-col mb-4">
<label htmlFor="name" className="block mb-2 font-bold text-sm text-gray-700">
Beschreibung :
</label>
<input type="text" id="name" value={name} onChange={(e) => setName(e.target.value)} className="block p-2 w-full border-2 border-gray-200 rounded-md text-sm" />
</div>
{/* React Select for Devices */}
<div className="flex flex-col mb-4">
<label htmlFor="deviceName" className="block mb-2 font-bold text-sm text-gray-700">
Gerät :
</label>
<Select
id="deviceName"
value={deviceName}
onChange={setDeviceName}
options={deviceOptions}
placeholder="Gerät auswählen..."
isClearable
styles={customStyles} // Apply custom styles here
/>
</div>
{/* React Select for POI Types */}
<div className="flex flex-col mb-4">
<label htmlFor="idPoiTyp" className="block mb-2 font-bold text-sm text-gray-700">
Typ:
</label>
<Select
id="idPoiTyp"
value={poiTypeId}
onChange={setPoiTypeId}
options={poiTypeOptions}
placeholder="Typ auswählen..."
styles={customStyles} // Apply custom styles here
/>
</div>
<div className="flex flex-row items-center justify-between mb-4">
<div className="flex flex-col items-center">
<label htmlFor="lat" className="block mb-2 text-xs text-gray-700">
Lat : {latitude}
</label>
</div>
<div className="flex flex-col items-center">
<label htmlFor="lng" className="block mb-2 text-xs text-gray-700">
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,256 @@
// /components/pois/PoiUpdateModal.js
import React, { useState, useEffect } from "react";
import Select from "react-select"; // Importiere react-select
import { useRecoilState } from "recoil";
import { selectedPoiState } from "../../store/atoms/poiState";
import { currentPoiState } from "../../store/atoms/currentPoiState";
import { mapLayersState } from "../../store/atoms/mapLayersState";
const PoiUpdateModal = ({ onClose, poiData, onSubmit }) => {
const currentPoi = useRecoilState(currentPoiState);
const selectedPoi = useRecoilState(selectedPoiState);
const [mapLayersVisibility] = useRecoilState(mapLayersState);
const [poiId, setPoiId] = useState(poiData ? poiData.idPoi : "");
const [name, setName] = useState(poiData ? poiData.name : "");
const [poiTypData, setPoiTypData] = useState([]);
const [poiTypeId, setPoiTypeId] = useState(null); // Verwende null für react-select
const [locationDeviceData, setLocationDeviceData] = useState([]);
const [filteredDevices, setFilteredDevices] = useState([]);
const [deviceName, setDeviceName] = useState(poiData ? poiData.deviceName : null); // Verwende null für react-select
const [idLD, setIdLD] = useState(poiData ? poiData.idLD : "");
const [description, setDescription] = useState(poiData ? poiData.description : "");
// Map von Systemnamen zu IDs (wie zuvor)
const systemNameToIdMap = {
TALAS: 1,
ECI: 2,
ULAF: 3,
GSMModem: 5,
CiscoRouter: 6,
WAGO: 7,
Siemens: 8,
OTDR: 9,
WDM: 10,
GMA: 11,
Messdatensammler: 12,
Messstellen: 13,
TALASICL: 100,
DAUZ: 110,
SMSFunkmodem: 111,
Basisgerät: 200,
};
useEffect(() => {
if (poiData) {
setPoiId(poiData.idPoi);
setName(poiData.name);
setPoiTypeId(poiData.idPoiTyp); // Setze den Typ-ID
setIdLD(poiData.idLD);
setDescription(poiData.description);
}
}, [poiData]);
// Fetch POI types and set the current POI type in the react-select dropdown
useEffect(() => {
const fetchPoiTypData = async () => {
try {
const response = await fetch("/api/talas_v5_DB/poiTyp/readPoiTyp");
const data = await response.json();
setPoiTypData(data);
// Prüfe den gespeicherten Typ im localStorage
const storedPoiType = localStorage.getItem("selectedPoiType");
// Finde den passenden Typ in den abgerufenen Daten und setze ihn als ausgewählt
if (storedPoiType) {
const matchingType = data.find((type) => type.name === storedPoiType);
if (matchingType) {
setPoiTypeId({ value: matchingType.idPoiTyp, label: matchingType.name });
}
} else if (poiData && poiData.idPoiTyp) {
// Falls kein Typ im localStorage ist, setze den Typ von poiData
const matchingType = data.find((type) => type.idPoiTyp === poiData.idPoiTyp);
if (matchingType) {
setPoiTypeId({ value: matchingType.idPoiTyp, label: matchingType.name });
}
}
} catch (error) {
console.error("Fehler beim Abrufen der poiTyp Daten:", error);
}
};
fetchPoiTypData();
}, [poiData]);
// Fetch location devices and pre-select the current device
useEffect(() => {
const fetchLocationDevices = async () => {
try {
const response = await fetch("/api/talas5/location_device");
const data = await response.json();
setLocationDeviceData(data);
filterDevices(data);
if (poiData && poiData.idLD) {
const selectedDevice = data.find((device) => device.idLD === poiData.idLD);
setDeviceName(selectedDevice ? { value: selectedDevice.name, label: selectedDevice.name } : null);
}
} catch (error) {
console.error("Fehler beim Abrufen der Standort- und Gerätedaten:", error);
}
};
fetchLocationDevices();
}, [poiData]);
// Funktion zum Filtern der Geräte basierend auf den aktiven Systemen (Layern)
const filterDevices = (devices) => {
const activeSystems = Object.keys(mapLayersVisibility).filter((system) => mapLayersVisibility[system]);
// Mappe aktive Systeme auf ihre ids
const activeSystemIds = activeSystems.map((system) => systemNameToIdMap[system]).filter((id) => id !== undefined);
// Filtere die Geräte nach aktiven Systemen basierend auf idsystem_typ
const filtered = devices.filter((device) => activeSystemIds.includes(device.idsystem_typ));
setFilteredDevices(filtered);
};
const handleSubmit = async (event) => {
event.preventDefault();
const idLDResponse = await fetch(`/api/talas_v5_DB/locationDevice/getDeviceId?deviceName=${encodeURIComponent(deviceName?.value)}`);
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?.value, // Den ausgewählten Typ mitsenden
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.");
}
};
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) {
onClose();
window.location.reload(); // Aktualisiert die Seite nach dem Löschen
} 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.");
}
}
};
// Erstelle Optionen für react-select
const poiTypeOptions = poiTypData.map((poiTyp) => ({
value: poiTyp.idPoiTyp,
label: poiTyp.name,
}));
const deviceOptions = filteredDevices.map((device) => ({
value: device.name,
label: device.name,
}));
// Custom styles for react-select
const customStyles = {
control: (provided) => ({
...provided,
width: "100%",
minWidth: "300px", // Minimum width for the dropdown
maxWidth: "100%", // Maximum width (you can adjust this if needed)
}),
menu: (provided) => ({
...provided,
width: "100%",
minWidth: "300px", // Ensure the dropdown menu stays at the minimum width
}),
};
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 flex-col mb-4">
<label htmlFor="description" className="block mb-2 font-bold text-sm text-gray-700">
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>
{/* React Select for Devices */}
<div className="flex flex-col mb-4">
<label htmlFor="deviceName" className="block mb-2 font-bold text-sm text-gray-700">
Gerät:
</label>
<Select
id="deviceName"
value={deviceName}
onChange={setDeviceName}
options={deviceOptions} // Options for filtering
placeholder="Gerät auswählen..."
isClearable={true} // Allow clearing the selection
styles={customStyles} // Apply custom styles
/>
</div>
{/* React Select for POI Types */}
<div className="flex flex-col mb-4">
<label htmlFor="idPoiTyp" className="block mb-2 font-bold text-sm text-gray-700">
Typ:
</label>
<Select
id="idPoiTyp"
value={poiTypeId}
onChange={setPoiTypeId}
options={poiTypeOptions} // Options for filtering
placeholder="Typ auswählen..."
isClearable={true}
styles={customStyles} // Apply custom styles
/>
</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,53 @@
// 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) => {
console.log("Marker wurde verschoben in addMarkersToMap");
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

@@ -1,22 +1,85 @@
import { useState, useCallback } from "react";
// /components/useMapContextMenu.js
import { toast } from "react-toastify";
import { zoomIn, zoomOut, centerHere } from "../utils/zoomAndCenterUtils"; // Assuming these are imported correctly
const useMapContextMenu = (map, hasRights, addStationCallback) => {
const [menuItemAdded, setMenuItemAdded] = useState(false);
const zoomInCallback = (e, map) => {
zoomIn(e, map);
};
const addItemsToMapContextMenu = useCallback(() => {
if (map && !menuItemAdded) {
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) => {
const editMode = localStorage.getItem("editMode") === "true";
hasRights = editMode ? hasRights : undefined;
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) => {
// Überprüfe den Bearbeitungsmodus in localStorage
const editMode = localStorage.getItem("editMode") === "true";
hasRights = editMode ? hasRights : undefined;
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),
});
// wenn localStorage Variable editMode true ist, dann wird der Button "POI hinzufügen" angezeigt
if (editMode) {
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),
callback: (event) => addStationCallback(event, hasRights, setShowPopup, setPopupCoordinates),
});
setMenuItemAdded(true); // Menüpunkt wurde hinzugefült, Zustand aktualisieren
}
}, [map, menuItemAdded, hasRights, addStationCallback]);
return { addItemsToMapContextMenu };
setMenuItemAdded(true);
}
};
export default useMapContextMenu;