From c57ae7717a86c38616dacd8b5d55f8147b72d24e Mon Sep 17 00:00:00 2001 From: Ismail Ali Date: Fri, 7 Mar 2025 20:52:00 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20Ger=C3=A4te-Daten=20aus=20Redux-Store?= =?UTF-8?q?=20in=20POI-Bearbeiten-Modal=20integriert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Geräte-Liste wird jetzt direkt aus dem Redux-Store (locationDevicesFromDB) verwendet. - Dropdown-Menü zeigt alle verfügbaren Geräte aus der Datenbank. - Beim Öffnen des Modals wird der vorher zugewiesene Gerätname automatisch ausgewählt (Pre-Selection). - Cleanup und Optimierung: Keine separaten API-Calls mehr im Modal. - Struktur verbessert durch Auslagerung der Lade-Logik in useInitLocationDevices Hook. --- components/PoiUpdateModal.js | 34 ++---- .../{ => mainComponent}/MapComponent.js | 104 +++++++++--------- .../hooks/useInitLocationDevices.js | 11 ++ components/pois/PoiUpdateModal.js | 46 ++++---- config/appVersion.js | 2 +- pages/index.js | 2 +- redux/api/fromDB/locationDevicesLoader.js | 8 ++ redux/api/fromDB/poiTypLoader.js | 0 redux/api/fromWebService/userSessionLoader.js | 0 redux/slices/db/locationDevicesFromDBSlice.js | 33 ++++++ redux/store.js | 2 + 11 files changed, 145 insertions(+), 97 deletions(-) rename components/{ => mainComponent}/MapComponent.js (91%) create mode 100644 components/mainComponent/hooks/useInitLocationDevices.js create mode 100644 redux/api/fromDB/locationDevicesLoader.js create mode 100644 redux/api/fromDB/poiTypLoader.js create mode 100644 redux/api/fromWebService/userSessionLoader.js create mode 100644 redux/slices/db/locationDevicesFromDBSlice.js diff --git a/components/PoiUpdateModal.js b/components/PoiUpdateModal.js index d9811ab53..93e07c287 100644 --- a/components/PoiUpdateModal.js +++ b/components/PoiUpdateModal.js @@ -1,11 +1,15 @@ -// pages/api/poiUpdateModal.js -// +// /components/PoiUpdateModal.js import React, { useState, useEffect } from "react"; import { useRecoilValue } from "recoil"; import { selectedPoiState } from "../redux/slices/selectedPoiSlice"; import { currentPoiState } from "../redux/slices/currentPoiSlice"; +import { fetchLocationDevicesFromDB } from "../redux/slices/db/locationDevicesFromDBSlice"; +import { useDispatch, useSelector } from "react-redux"; const PoiUpdateModal = ({ onClose, poiData }) => { + const dispatch = useDispatch(); + const devices = useSelector((state) => state.locationDevicesFromDB.devices); + const currentPoi = useRecoilValue(currentPoiState); const selectedPoi = useRecoilValue(selectedPoiState); const [poiId, setPoiId] = useState(poiData ? poiData.idPoi : ""); @@ -19,6 +23,10 @@ const PoiUpdateModal = ({ onClose, poiData }) => { const [description, setDescription] = useState(poiData ? poiData.description : ""); + useEffect(() => { + dispatch(fetchLocationDevicesFromDB()); + }, [dispatch]); + // Log the initial POI data useEffect(() => { if (poiData) { @@ -117,28 +125,6 @@ const PoiUpdateModal = ({ onClose, poiData }) => { fetchPoiTypData(); }, [selectedPoi]); - // Fetch device data um den Gerät Namen in den dropdown menu anzuzeigen also erstmal die Liste der Geräte abrufen - 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(); - //console.log("Standort- und Gerätedaten:", data); - setLocationDeviceData(data); - console.log("Standort- und Gerätedaten poiData:", poiData); - if (poiData && poiData.idLD) { - 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); - } - }; - fetchData(); - }, []); //-------------------------------------------------------------------------------------------- // Fetch device name basierend auf der Geräte-ID diff --git a/components/MapComponent.js b/components/mainComponent/MapComponent.js similarity index 91% rename from components/MapComponent.js rename to components/mainComponent/MapComponent.js index 38eaacd33..159960049 100644 --- a/components/MapComponent.js +++ b/components/mainComponent/MapComponent.js @@ -4,71 +4,72 @@ import L 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 config from "../../config/config.js"; import "leaflet.smooth_marker_bouncing"; import OverlappingMarkerSpiderfier from "overlapping-marker-spiderfier-leaflet"; //sieht deaktiviert aber ist das nicht so und wird benötigt import "react-toastify/dist/ReactToastify.css"; -import DataSheet from "./DataSheet.js"; +import DataSheet from "../DataSheet.js"; import { useRecoilState, useRecoilValue, useSetRecoilState } from "recoil"; -import AddPoiModalWindow from "./pois/AddPoiModalWindow.js"; +import AddPoiModalWindow from "../pois/AddPoiModalWindow.js"; import { InformationCircleIcon } from "@heroicons/react/20/solid"; -import PoiUpdateModal from "./pois/PoiUpdateModal.js"; +import PoiUpdateModal from "../pois/PoiUpdateModal.js"; import { ToastContainer, toast } from "react-toastify"; -import plusRoundIcon from "./PlusRoundIcon.js"; -import { createAndSetDevices } from "../utils/createAndSetDevices.js"; -import { restoreMapSettings, checkOverlappingMarkers } from "../utils/mapUtils.js"; -import { APP_VERSION } from "../config/appVersion"; -import * as layers from "../config/layers.js"; -import { initializeMap } from "../utils/initializeMap.js"; -import addItemsToMapContextMenu from "./useMapContextMenu.js"; -import useGmaMarkersLayer from "../hooks/layers/useGmaMarkersLayer.js"; // Import the custom hook -import useSmsfunkmodemMarkersLayer from "../hooks/layers/useSmsfunkmodemMarkersLayer.js"; -import useBereicheMarkersLayer from "../hooks/layers/useBereicheMarkersLayer.js"; -import { setupPolylines } from "../utils/setupPolylines.js"; -import { setupPOIs } from "../utils/setupPOIs.js"; -import VersionInfoModal from "./VersionInfoModal.js"; -import useDrawLines from "../hooks/layers/useDrawLines"; -import useFetchPoiData from "../hooks/useFetchPoiData"; -import usePoiTypData from "../hooks/usePoiTypData"; -import useLayerVisibility from "../hooks/useLayerVisibility"; -import useLineData from "../hooks/useLineData.js"; +import plusRoundIcon from "../PlusRoundIcon.js"; +import { createAndSetDevices } from "../../utils/createAndSetDevices.js"; +import { restoreMapSettings, checkOverlappingMarkers } from "../../utils/mapUtils.js"; +import { APP_VERSION } from "../../config/appVersion.js"; +import * as layers from "../../config/layers.js"; +import { initializeMap } from "../../utils/initializeMap.js"; +import addItemsToMapContextMenu from "../useMapContextMenu.js"; +import useGmaMarkersLayer from "../../hooks/layers/useGmaMarkersLayer.js"; // Import the custom hook +import useSmsfunkmodemMarkersLayer from "../../hooks/layers/useSmsfunkmodemMarkersLayer.js"; +import useBereicheMarkersLayer from "../../hooks/layers/useBereicheMarkersLayer.js"; +import { setupPolylines } from "../../utils/setupPolylines.js"; +import { setupPOIs } from "../../utils/setupPOIs.js"; +import VersionInfoModal from "../VersionInfoModal.js"; +import useDrawLines from "../../hooks/layers/useDrawLines.js"; +import useFetchPoiData from "../../hooks/useFetchPoiData.js"; +import usePoiTypData from "../../hooks/usePoiTypData.js"; +import useLayerVisibility from "../../hooks/useLayerVisibility.js"; +import useLineData from "../../hooks/useLineData.js"; //import { useCreateAndSetDevices } from "../hooks/useCreateAndSetDevices"; -import { useMapComponentState } from "../hooks/useMapComponentState"; -import { disablePolylineEvents, enablePolylineEvents } from "../utils/setupPolylines"; -import { updateLocation } from "../utils/updateBereichUtil"; -import { initGeocoderFeature } from "../components/features/GeocoderFeature"; +import { useMapComponentState } from "../../hooks/useMapComponentState.js"; +import { disablePolylineEvents, enablePolylineEvents } from "../../utils/setupPolylines.js"; +import { updateLocation } from "../../utils/updateBereichUtil.js"; +import { initGeocoderFeature } from "../features/GeocoderFeature.js"; //-------------------------------------------- //import { currentPoiState } from "../redux/slices/currentPoiSlice.js"; -import { selectGisStationsStaticDistrict, setGisStationsStaticDistrict } from "../redux/slices/webService/gisStationsStaticDistrictSlice"; -import { mapIdState, userIdState } from "../redux/slices/urlParameterSlice.js"; -import { poiLayerVisibleState } from "../redux/slices/poiLayerVisibleSlice.js"; -import { selectedPoiState } from "../redux/slices/selectedPoiSlice.js"; -import { poiReadFromDbTriggerAtom } from "../redux/slices/poiReadFromDbTriggerSlice"; -import { gisStationsStaticDistrictState } from "../redux/slices/webService/gisStationsStaticDistrictSlice"; -import { gisSystemStaticState } from "../redux/slices/webService/gisSystemStaticSlice"; -import { mapLayersState } from "../redux/slices/mapLayersSlice"; -import { selectedAreaState } from "../redux/slices/selectedAreaSlice"; -import { zoomTriggerState } from "../redux/slices/zoomTriggerSlice.js"; -import { polylineEventsDisabledState } from "../redux/slices/polylineEventsDisabledSlice"; -import { polylineLayerVisibleState } from "../redux/slices/polylineLayerVisibleSlice"; +import { selectGisStationsStaticDistrict, setGisStationsStaticDistrict } from "../../redux/slices/webService/gisStationsStaticDistrictSlice.js"; +import { mapIdState, userIdState } from "../../redux/slices/urlParameterSlice.js"; +import { poiLayerVisibleState } from "../../redux/slices/poiLayerVisibleSlice.js"; +import { selectedPoiState } from "../../redux/slices/selectedPoiSlice.js"; +import { poiReadFromDbTriggerAtom } from "../../redux/slices/poiReadFromDbTriggerSlice.js"; +import { gisStationsStaticDistrictState } from "../../redux/slices/webService/gisStationsStaticDistrictSlice.js"; +import { gisSystemStaticState } from "../../redux/slices/webService/gisSystemStaticSlice.js"; +import { mapLayersState } from "../../redux/slices/mapLayersSlice.js"; +import { selectedAreaState } from "../../redux/slices/selectedAreaSlice.js"; +import { zoomTriggerState } from "../../redux/slices/zoomTriggerSlice.js"; +import { polylineEventsDisabledState } from "../../redux/slices/polylineEventsDisabledSlice.js"; +import { polylineLayerVisibleState } from "../../redux/slices/polylineLayerVisibleSlice.js"; //-------------------------------------------- import { useSelector, useDispatch } from "react-redux"; -import { selectCurrentPoi, setCurrentPoi, clearCurrentPoi } from "../redux/slices/currentPoiSlice"; -import CoordinateInput from "./CoordinateInput"; -import CoordinateModal from "./CoordinateModal"; -import CoordinatePopup from "./CoordinatePopup"; +import { selectCurrentPoi, setCurrentPoi, clearCurrentPoi } from "../../redux/slices/currentPoiSlice.js"; +import CoordinateInput from "../CoordinateInput.js"; +import CoordinateModal from "../CoordinateModal.js"; +import CoordinatePopup from "../CoordinatePopup.js"; //------------------------Daten aus API-------------------- -import { fetchUserRights } from "../services/api/fetchUserRights.js"; -import { fetchPoiData } from "../services/api/fetchPoiData.js"; -import { fetchGisStationsStaticDistrict } from "../services/api/fetchGisStationsStaticDistrict.js"; +import { fetchUserRights } from "../../services/api/fetchUserRights.js"; +import { fetchPoiData } from "../../services/api/fetchPoiData.js"; +import { fetchGisStationsStaticDistrict } from "../../services/api/fetchGisStationsStaticDistrict.js"; -import { fetchGisStationsStatusDistrict } from "../services/api/fetchGisStationsStatusDistrict.js"; +import { fetchGisStationsStatusDistrict } from "../../services/api/fetchGisStationsStatusDistrict.js"; -import { fetchGisStationsMeasurements } from "../services/api/fetchGisStationsMeasurements.js"; -import { fetchGisSystemStatic } from "../services/api/fetchGisSystemStatic.js"; -import { usePolylineTooltipLayer } from "../hooks/usePolylineTooltipLayer"; -import { selectPolylineVisible, setPolylineVisible } from "../redux/slices/polylineLayerVisibleSlice"; +import { fetchGisStationsMeasurements } from "../../services/api/fetchGisStationsMeasurements.js"; +import { fetchGisSystemStatic } from "../../services/api/fetchGisSystemStatic.js"; +import { usePolylineTooltipLayer } from "../../hooks/usePolylineTooltipLayer.js"; +import { selectPolylineVisible, setPolylineVisible } from "../../redux/slices/polylineLayerVisibleSlice.js"; +import { useInitLocationDevices } from "./hooks/useInitLocationDevices"; const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => { const dispatch = useDispatch(); @@ -996,6 +997,9 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => { }, [dispatch]); //---------------------------------------------- + // speichere location devices in redux store + useInitLocationDevices(); + //-------------------------------------- return ( <> diff --git a/components/mainComponent/hooks/useInitLocationDevices.js b/components/mainComponent/hooks/useInitLocationDevices.js new file mode 100644 index 000000000..3a5ce68dc --- /dev/null +++ b/components/mainComponent/hooks/useInitLocationDevices.js @@ -0,0 +1,11 @@ +import { useEffect } from "react"; +import { useDispatch } from "react-redux"; +import { fetchLocationDevicesFromDB } from "../../../redux/slices/db/locationDevicesFromDBSlice"; + +export const useInitLocationDevices = () => { + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(fetchLocationDevicesFromDB()); + }, [dispatch]); +}; diff --git a/components/pois/PoiUpdateModal.js b/components/pois/PoiUpdateModal.js index 801b81d22..f0ae47c89 100644 --- a/components/pois/PoiUpdateModal.js +++ b/components/pois/PoiUpdateModal.js @@ -5,10 +5,13 @@ import { useRecoilState } from "recoil"; import { selectedPoiState } from "../../redux/slices/selectedPoiSlice"; import { currentPoiState } from "../../redux/slices/currentPoiSlice"; import { mapLayersState } from "../../redux/slices/mapLayersSlice"; -import { useSelector } from "react-redux"; import { selectCurrentPoi } from "../../redux/slices/currentPoiSlice"; +import { fetchLocationDevicesFromDB } from "../../redux/slices/db/locationDevicesFromDBSlice"; +import { useSelector, useDispatch } from "react-redux"; const PoiUpdateModal = ({ onClose, poiData, onSubmit }) => { + const dispatch = useDispatch(); + const currentPoi = useSelector(selectCurrentPoi); const selectedPoi = useRecoilState(selectedPoiState); const [mapLayersVisibility] = useRecoilState(mapLayersState); @@ -43,6 +46,12 @@ const PoiUpdateModal = ({ onClose, poiData, onSubmit }) => { Basisgerät: 200, }; + const devices = useSelector((state) => state.locationDevicesFromDB.devices); + + useEffect(() => { + dispatch(fetchLocationDevicesFromDB()); + }, [dispatch]); + useEffect(() => { if (poiData) { setPoiId(poiData.idPoi); @@ -86,23 +95,10 @@ const PoiUpdateModal = ({ onClose, poiData, onSubmit }) => { // 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]); + if (devices.length > 0) { + filterDevices(devices); // <-- Filter direkt die Redux-Devices + } + }, [devices]); // Funktion zum Filtern der Geräte basierend auf den aktiven Systemen (Layern) const filterDevices = (devices) => { @@ -177,10 +173,18 @@ const PoiUpdateModal = ({ onClose, poiData, onSubmit }) => { })) : []; // Falls kein Array, dann leeres Array zurückgeben - const deviceOptions = filteredDevices.map((device) => ({ - value: device.name, - label: device.name, + const deviceOptions = devices.map((device) => ({ + value: device.idLD, // idLD ist die eindeutige ID des Geräts + label: device.name, // name ist der Anzeigename im Dropdown })); + useEffect(() => { + if (poiData && devices.length > 0) { + const selectedDevice = devices.find((device) => device.idLD === poiData.idLD); + if (selectedDevice) { + setDeviceName({ value: selectedDevice.idLD, label: selectedDevice.name }); + } + } + }, [poiData, devices]); // Custom styles for react-select const customStyles = { diff --git a/config/appVersion.js b/config/appVersion.js index 478b913dd..357f37596 100644 --- a/config/appVersion.js +++ b/config/appVersion.js @@ -1,2 +1,2 @@ // /config/appVersion -export const APP_VERSION = "1.1.22"; +export const APP_VERSION = "1.1.23"; diff --git a/pages/index.js b/pages/index.js index 35513d35d..c47f9627f 100644 --- a/pages/index.js +++ b/pages/index.js @@ -5,7 +5,7 @@ import { useRecoilState, useRecoilValue } from "recoil"; import { readPoiMarkersStore } from "../redux/slices/readPoiMarkersStoreSlice.js"; import { poiReadFromDbTriggerAtom } from "../redux/slices/poiReadFromDbTriggerSlice"; -const MapComponentWithNoSSR = dynamic(() => import("../components/MapComponent"), { ssr: false }); +const MapComponentWithNoSSR = dynamic(() => import("../components/mainComponent/MapComponent"), { ssr: false }); const TestScriptWithNoSSR = dynamic(() => import("../components/TestScript"), { ssr: false }); export default function Home() { diff --git a/redux/api/fromDB/locationDevicesLoader.js b/redux/api/fromDB/locationDevicesLoader.js new file mode 100644 index 000000000..a7c4c5854 --- /dev/null +++ b/redux/api/fromDB/locationDevicesLoader.js @@ -0,0 +1,8 @@ +// /redux/api/fromDB/locationDevicesLoader.js +export const fetchLocationDevices = async () => { + const response = await fetch("/api/talas_v5_DB/locationDevice/locationDevices"); + if (!response.ok) { + throw new Error("Geräteliste konnte nicht geladen werden"); + } + return await response.json(); +}; diff --git a/redux/api/fromDB/poiTypLoader.js b/redux/api/fromDB/poiTypLoader.js new file mode 100644 index 000000000..e69de29bb diff --git a/redux/api/fromWebService/userSessionLoader.js b/redux/api/fromWebService/userSessionLoader.js new file mode 100644 index 000000000..e69de29bb diff --git a/redux/slices/db/locationDevicesFromDBSlice.js b/redux/slices/db/locationDevicesFromDBSlice.js new file mode 100644 index 000000000..efff363f9 --- /dev/null +++ b/redux/slices/db/locationDevicesFromDBSlice.js @@ -0,0 +1,33 @@ +// /redux/slices/db/locationDevicesFromDBSlice.js +import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; +import { fetchLocationDevices } from "../../api/fromDB/locationDevicesLoader"; + +export const fetchLocationDevicesFromDB = createAsyncThunk("locationDevicesFromDB/fetchLocationDevicesFromDB", async () => { + return fetchLocationDevices(); +}); + +const locationDevicesFromDBSlice = createSlice({ + name: "locationDevicesFromDB", + initialState: { + devices: [], + status: "idle", + error: null, + }, + reducers: {}, + extraReducers: (builder) => { + builder + .addCase(fetchLocationDevicesFromDB.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchLocationDevicesFromDB.fulfilled, (state, action) => { + state.status = "succeeded"; + state.devices = action.payload; // <-- Hier landen die Daten + }) + .addCase(fetchLocationDevicesFromDB.rejected, (state, action) => { + state.status = "failed"; + state.error = action.error.message; + }); + }, +}); + +export default locationDevicesFromDBSlice.reducer; diff --git a/redux/store.js b/redux/store.js index 1544c7275..5894f6049 100644 --- a/redux/store.js +++ b/redux/store.js @@ -3,6 +3,7 @@ import lineVisibilityReducer from "./slices/lineVisibilitySlice"; import currentPoiReducer from "./slices/currentPoiSlice"; import gisStationsStaticDistrictReducer from "./slices/webService/gisStationsStaticDistrictSlice"; import polylineLayerVisibleReducer from "./slices/polylineLayerVisibleSlice"; +import locationDevicesFromDBReducer from "./slices/db/locationDevicesFromDBSlice"; export const store = configureStore({ reducer: { @@ -10,5 +11,6 @@ export const store = configureStore({ currentPoi: currentPoiReducer, gisStationsStaticDistrict: gisStationsStaticDistrictReducer, polylineLayerVisible: polylineLayerVisibleReducer, + locationDevicesFromDB: locationDevicesFromDBReducer, }, });