diff --git a/CHANGELOG.md b/CHANGELOG.md index f0553c8e0..52d85eafe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,26 @@ Alle bedeutenden Änderungen an diesem Projekt werden in dieser Datei dokumentie --- +## [1.1.144] – 2025-05-22 + +### Fixed + +- ✅ Fehlerhafte Webservice-Antwortstruktur in `fetchGisLinesStatusService.js` korrigiert + - Erwartet wurde `json.Lines`, geliefert wurde aber `json.Statis` + - Daten landen jetzt korrekt im Redux Slice `gisLinesStatus` + +### Architecture + +- 🔁 Vollständige Redux-Anbindung von Linienstatus über `fetchGisLinesStatusThunk` +- 🧠 Daten aus WebService `GisLinesStatus` werden zentral über Redux verwaltet +- 💡 Fehlerbehandlung und JSON-Parsing robuster gestaltet + +### Version + +- 📦 Version erhöht auf **1.1.144** + +--- + ## [1.1.139] – 2025-05-22 ### Changed diff --git a/components/AddPOIModal.js b/components/AddPOIModal.js index 821c0acbf..bbc68df85 100644 --- a/components/AddPOIModal.js +++ b/components/AddPOIModal.js @@ -2,7 +2,7 @@ import React, { useState, useEffect, use } from "react"; import ReactDOM from "react-dom"; import { setPoiMarkers } from "../redux/slices/readPoiMarkersStoreSlice"; -import { selectGisStationsStaticDistrict } from "../redux/slices/webService/gisStationsStaticDistrictSlice"; +import { selectGisStationsStaticDistrict } from "../redux/slices/webservice/gisStationsStaticDistrictSlice"; import { useDispatch, useSelector } from "react-redux"; import { fetchPoiTypes } from "../redux/slices/database/poiTypesSlice"; import { incrementTrigger } from "../redux/slices/poiReadFromDbTriggerSlice"; diff --git a/components/MapLayersControlPanel.js b/components/MapLayersControlPanel.js index 1d5971092..49bf9a6f0 100644 --- a/components/MapLayersControlPanel.js +++ b/components/MapLayersControlPanel.js @@ -4,8 +4,8 @@ import { setSelectedArea } from "../redux/slices/selectedAreaSlice"; import EditModeToggle from "./EditModeToggle"; import { useSelector, useDispatch } from "react-redux"; import { selectPolylineVisible, setPolylineVisible } from "../redux/slices/polylineLayerVisibleSlice"; -import { selectGisSystemStatic } from "../redux/slices/webService/gisSystemStaticSlice"; -import { selectGisStationsStaticDistrict } from "../redux/slices/webService/gisStationsStaticDistrictSlice"; +import { selectGisSystemStatic } from "../redux/slices/webservice/gisSystemStaticSlice"; +import { selectGisStationsStaticDistrict } from "../redux/slices/webservice/gisStationsStaticDistrictSlice"; import { selectMapLayersState, setLayerVisibility } from "../redux/slices/mapLayersSlice"; import { setVisible } from "../redux/slices/poiLayerVisibleSlice"; import { incrementZoomTrigger } from "../redux/slices/zoomTriggerSlice"; diff --git a/components/mainComponent/MapComponent.js b/components/mainComponent/MapComponent.js index f9872ee08..d636ff24a 100644 --- a/components/mainComponent/MapComponent.js +++ b/components/mainComponent/MapComponent.js @@ -38,8 +38,8 @@ import CoordinatePopup from "../CoordinatePopup.js"; //------------------------Daten aus API-------------------- import { fetchPoiDataService } from "../../services/database/fetchPoiDataService.js"; import { selectPolylineVisible, setPolylineVisible } from "../../redux/slices/polylineLayerVisibleSlice.js"; -import { selectGisStationsStaticDistrict } from "../../redux/slices/webService/gisStationsStaticDistrictSlice"; -import { selectGisSystemStatic, setGisSystemStatic } from "../../redux/slices/webService/gisSystemStaticSlice"; +import { selectGisStationsStaticDistrict } from "../../redux/slices/webservice/gisStationsStaticDistrictSlice.js"; +import { selectGisSystemStatic, setGisSystemStatic } from "../../redux/slices/webservice/gisSystemStaticSlice.js"; import ShowAddStationPopup from "../AddPOIModal.js"; import AddPOIOnPolyline from "../AddPOIOnPolyline"; import { enablePolylineEvents, disablePolylineEvents } from "../../utils/polylines/eventHandlers"; @@ -62,7 +62,10 @@ import { fetchLocationDevicesThunk } from "../../redux/thunks/database/fetchLoca import { fetchPriorityConfigThunk } from "../../redux/thunks/database/fetchPriorityConfigThunk"; import { selectPriorityConfig } from "../../redux/slices/database/priorityConfigSlice"; import { fetchGisLinesThunk } from "../../redux/thunks/database/fetchGisLinesThunk"; +import { fetchGisLinesStatusThunk } from "../../redux/thunks/webservice/fetchGisLinesStatusThunk"; +import { fetchUserRightsThunk } from "../../redux/thunks/webservice/fetchUserRightsThunk"; import { selectGisLines } from "../../redux/slices/database/gisLinesSlice"; +import { selectGisLinesStatus } from "../../redux/slices/webservice/gisLinesStatusSlice"; const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => { //------------------------------- @@ -90,6 +93,11 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => { const linesData = useSelector((state) => state.gisLines.data); const gisLinesStatus = useSelector((state) => state.gisLines.status); + const { data: gisLinesStatusData, status: statusGisLinesStatus } = useSelector(selectGisLinesStatus); + useEffect(() => { + console.log("✅ Redux: gisLinesStatusData:", gisLinesStatusData); + }, [gisLinesStatusData]); + //------------------------------- const { deviceName, setDeviceName } = useMapComponentState(); const [locationDeviceData, setLocationDeviceData] = useState([]); @@ -228,6 +236,14 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => { } }, [gisLinesStatus, dispatch]); //-------------------------------------------------------- + useEffect(() => { + dispatch(fetchGisLinesStatusThunk()); + }, [dispatch]); + //--------------------------------------------------------- + useEffect(() => { + dispatch(fetchUserRightsThunk()); + }, [dispatch]); + //---------------------------------------------------- useEffect(() => { if (linesData && Array.isArray(linesData)) { diff --git a/config/appVersion.js b/config/appVersion.js index dddc471d4..f1120aef5 100644 --- a/config/appVersion.js +++ b/config/appVersion.js @@ -1,2 +1,2 @@ // /config/appVersion -export const APP_VERSION = "1.1.143"; +export const APP_VERSION = "1.1.145"; diff --git a/config/config.js b/config/config.js index 83f9f3dbf..f72db80f9 100644 --- a/config/config.js +++ b/config/config.js @@ -1,30 +1,32 @@ // Datei: /config/config.js import { BASE_URL } from "../config/paths"; -// Definieren der grundlegenden Umgebungseinstellungen und Konfigurationen der Karte - -const standardSideMenu = true; -const fullSideMenu = false; - -// Server-URL aus Umgebungsvariable holen (nur bei echter API benötigt) -const mode = process.env.NEXT_PUBLIC_API_PORT_MODE; - -const serverURL = mode === "dev" ? `${window.location.protocol}//${window.location.hostname}:80` : `${window.location.origin}`; - -if (!serverURL && !isMockMode()) { - throw new Error("Die Umgebungsvariable ist nicht gesetzt!"); -} -console.log("%c 1- serverURL in config:", "color: #006400;", serverURL); - -// Initialisieren von Variablen, die später im Browserkontext gesetzt werden -let windowHeight, url_string, url, idMap, idUser; -let mapGisSystemStaticUrl, mapDataIconUrl, webserviceGisLinesStatusUrl; - // Prüfen, ob Mock-Modus aktiv ist function isMockMode() { return process.env.NEXT_PUBLIC_USE_MOCK_API === "true"; } +// Definieren der grundlegenden Umgebungseinstellungen und Konfigurationen der Karte + +const mode = process.env.NEXT_PUBLIC_API_PORT_MODE; +let serverURL = ""; + +if (typeof window !== "undefined") { + serverURL = mode === "dev" ? `${window.location.protocol}//${window.location.hostname}:80` : `${window.location.origin}`; +} + +if (typeof window !== "undefined" && !serverURL && !isMockMode()) { + throw new Error("Die Umgebungsvariable ist nicht gesetzt!"); +} + +if (typeof window !== "undefined") { + console.log("%c 1- serverURL in config:", "color: #006400;", serverURL); +} + +// Initialisieren von Variablen, die später im Browserkontext gesetzt werden +let windowHeight, url_string, url, idMap, idUser; +let webserviceGisLinesStatusUrl; + // URL-Setup - dynamisch abhängig von Mock oder Echtbetrieb if (typeof window !== "undefined") { windowHeight = window.innerHeight; @@ -39,23 +41,14 @@ if (typeof window !== "undefined") { if (isMockMode()) { // Mock-Daten jetzt über API-Endpunkte aus pages/api/mockData/webService/ - - mapGisSystemStaticUrl = "/api/mockData/webService/GisSystemStaticMock"; - mapDataIconUrl = "/api/mockData/webService/GetIconsStaticMock"; webserviceGisLinesStatusUrl = "/api/mockData/webService/GisLinesStatusMock"; console.log("📡 Mock-Mode aktiv: Daten werden aus /api/mockData/webService geladen."); } else { // Echte URLs zur Webservice-API - - mapGisSystemStaticUrl = `${serverURL}${BASE_URL}/ClientData/WebserviceMap.asmx/GisSystemStatic?idMap=${idMap}&idUser=${idUser}`; - - mapDataIconUrl = `${serverURL}${BASE_URL}/ClientData/WebserviceMap.asmx/GetIconsStatic`; - webserviceGisLinesStatusUrl = `${serverURL}${BASE_URL}/ClientData/WebServiceMap.asmx/GisLinesStatus?idMap=${idMap}`; - console.log("🌐 Echt-Mode aktiv: Daten werden von der API geholt."); } } // Export der Variablen und URLs -export { standardSideMenu, fullSideMenu, serverURL, windowHeight, url_string, url, idMap, idUser, mapGisSystemStaticUrl, mapDataIconUrl, webserviceGisLinesStatusUrl, isMockMode }; +export { serverURL, windowHeight, url_string, url, idMap, idUser, webserviceGisLinesStatusUrl, isMockMode }; diff --git a/redux/slices/database/gisLinesSlice.js b/redux/slices/database/gisLinesSlice.js index e77147968..8685b5445 100644 --- a/redux/slices/database/gisLinesSlice.js +++ b/redux/slices/database/gisLinesSlice.js @@ -1,3 +1,4 @@ +// /redux/slices/database/gisLinesSlice.js import { createSlice } from "@reduxjs/toolkit"; import { fetchGisLinesThunk } from "../../thunks/database/fetchGisLinesThunk"; diff --git a/redux/slices/webService/gisLinesStatusSlice.js b/redux/slices/webService/gisLinesStatusSlice.js new file mode 100644 index 000000000..6bb3c0587 --- /dev/null +++ b/redux/slices/webService/gisLinesStatusSlice.js @@ -0,0 +1,33 @@ +// /redux/slices/webservice/gisLinesStatusSlice.js +import { createSlice } from "@reduxjs/toolkit"; +import { fetchGisLinesStatusThunk } from "../../thunks/webservice/fetchGisLinesStatusThunk"; + +const initialState = { + data: [], + status: "idle", + error: null, +}; + +export const gisLinesStatusSlice = createSlice({ + name: "gisLinesStatus", + initialState, + reducers: {}, + extraReducers: (builder) => { + builder + .addCase(fetchGisLinesStatusThunk.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchGisLinesStatusThunk.fulfilled, (state, action) => { + state.status = "succeeded"; + state.data = action.payload; + }) + .addCase(fetchGisLinesStatusThunk.rejected, (state, action) => { + state.status = "failed"; + state.error = action.payload; + }); + }, +}); + +export default gisLinesStatusSlice.reducer; + +export const selectGisLinesStatus = (state) => state.gisLinesStatus; diff --git a/redux/slices/webService/userRightsSlice.js b/redux/slices/webService/userRightsSlice.js new file mode 100644 index 000000000..767d6599a --- /dev/null +++ b/redux/slices/webService/userRightsSlice.js @@ -0,0 +1,34 @@ +// /redux/slices/webservice/userRightsSlice.js +import { createSlice } from "@reduxjs/toolkit"; +import { fetchUserRightsThunk } from "../../thunks/webservice/fetchUserRightsThunk"; + +const initialState = { + rights: [], + status: "idle", + error: null, +}; + +export const userRightsSlice = createSlice({ + name: "userRights", + initialState, + reducers: {}, + extraReducers: (builder) => { + builder + .addCase(fetchUserRightsThunk.pending, (state) => { + state.status = "loading"; + }) + .addCase(fetchUserRightsThunk.fulfilled, (state, action) => { + state.status = "succeeded"; + state.rights = action.payload; + }) + .addCase(fetchUserRightsThunk.rejected, (state, action) => { + state.status = "failed"; + state.error = action.payload; + }); + }, +}); + +export default userRightsSlice.reducer; + +export const selectUserRights = (state) => state.userRights.rights; +export const selectUserRightsStatus = (state) => state.userRights.status; diff --git a/redux/store.js b/redux/store.js index 8577fa1cb..9bdb58013 100644 --- a/redux/store.js +++ b/redux/store.js @@ -3,13 +3,6 @@ import { configureStore } from "@reduxjs/toolkit"; import lineVisibilityReducer from "./slices/lineVisibilitySlice"; import currentPoiReducer from "./slices/currentPoiSlice"; import polylineLayerVisibleReducer from "./slices/polylineLayerVisibleSlice"; -import locationDevicesFromDBReducer from "./slices/database/locationDevicesFromDBSlice"; -import gisStationsStaticDistrictReducer from "./slices/webService/gisStationsStaticDistrictSlice"; -import gisStationsStatusDistrictReducer from "./slices/webService/gisStationsStatusDistrictSlice"; -import gisStationsMeasurementsReducer from "./slices/webService/gisStationsMeasurementsSlice"; -import gisSystemStaticReducer from "./slices/webService/gisSystemStaticSlice"; - -import poiTypesReducer from "./slices/database/poiTypesSlice"; import addPoiOnPolylineReducer from "./slices/addPoiOnPolylineSlice"; import polylineContextMenuReducer from "./slices/polylineContextMenuSlice"; import selectedPoiReducer from "./slices/selectedPoiSlice"; @@ -22,8 +15,18 @@ import readPoiMarkersStoreReducer from "./slices/readPoiMarkersStoreSlice"; import selectedAreaReducer from "./slices/selectedAreaSlice"; import zoomTriggerReducer from "./slices/zoomTriggerSlice"; import urlParameterReducer from "./slices/urlParameterSlice"; +//-----database------------ import priorityConfigReducer from "./slices/database/priorityConfigSlice"; import gisLinesReducer from "./slices/database/gisLinesSlice"; +import poiTypesReducer from "./slices/database/poiTypesSlice"; +import locationDevicesFromDBReducer from "./slices/database/locationDevicesFromDBSlice"; +//----webservice------------ +import gisStationsStaticDistrictReducer from "./slices/webservice/gisStationsStaticDistrictSlice"; +import gisStationsStatusDistrictReducer from "./slices/webservice/gisStationsStatusDistrictSlice"; +import gisStationsMeasurementsReducer from "./slices/webservice/gisStationsMeasurementsSlice"; +import gisSystemStaticReducer from "./slices/webservice/gisSystemStaticSlice"; +import gisLinesStatusReducer from "./slices/webservice/gisLinesStatusSlice"; +import userRightsReducer from "./slices/webservice/userRightsSlice"; export const store = configureStore({ reducer: { @@ -50,5 +53,7 @@ export const store = configureStore({ urlParameter: urlParameterReducer, priorityConfig: priorityConfigReducer, gisLines: gisLinesReducer, + gisLinesStatus: gisLinesStatusReducer, + userRights: userRightsReducer, }, }); diff --git a/redux/thunks/database/fetchGisLinesThunk.js b/redux/thunks/database/fetchGisLinesThunk.js index 7bc308315..b2381fee1 100644 --- a/redux/thunks/database/fetchGisLinesThunk.js +++ b/redux/thunks/database/fetchGisLinesThunk.js @@ -1,3 +1,4 @@ +// /redux/thunks/database/fetchGisLinesThunk.js import { createAsyncThunk } from "@reduxjs/toolkit"; import { fetchGisLinesService } from "../../../services/database/fetchGisLinesService"; diff --git a/redux/thunks/webservice/fetchGisLinesStatusThunk.js b/redux/thunks/webservice/fetchGisLinesStatusThunk.js new file mode 100644 index 000000000..574736727 --- /dev/null +++ b/redux/thunks/webservice/fetchGisLinesStatusThunk.js @@ -0,0 +1,11 @@ +// /redux/thunks/webservice/fetchGisLinesStatusThunk.js +import { createAsyncThunk } from "@reduxjs/toolkit"; +import { fetchGisLinesStatusService } from "../../../services/webservice/fetchGisLinesStatusService"; + +export const fetchGisLinesStatusThunk = createAsyncThunk("gisLinesStatus/fetch", async (_, thunkAPI) => { + try { + return await fetchGisLinesStatusService(); + } catch (error) { + return thunkAPI.rejectWithValue(error.message); + } +}); diff --git a/redux/thunks/webservice/fetchUserRightsThunk.js b/redux/thunks/webservice/fetchUserRightsThunk.js new file mode 100644 index 000000000..6301fc733 --- /dev/null +++ b/redux/thunks/webservice/fetchUserRightsThunk.js @@ -0,0 +1,11 @@ +// /redux/thunks/webservice/fetchUserRightsThunk.js +import { createAsyncThunk } from "@reduxjs/toolkit"; +import { fetchUserRightsService } from "../../../services/webservice/fetchUserRightsService"; + +export const fetchUserRightsThunk = createAsyncThunk("userRights/fetch", async (_, thunkAPI) => { + try { + return await fetchUserRightsService(); + } catch (error) { + return thunkAPI.rejectWithValue(error.message); + } +}); diff --git a/services/webservice/fetchGisLinesStatusService.js b/services/webservice/fetchGisLinesStatusService.js new file mode 100644 index 000000000..851077eb3 --- /dev/null +++ b/services/webservice/fetchGisLinesStatusService.js @@ -0,0 +1,27 @@ +// /services/webservice/fetchGisLinesStatusService.js +export const fetchGisLinesStatusService = async () => { + const mode = process.env.NEXT_PUBLIC_API_PORT_MODE; + const baseUrl = mode === "dev" ? `${window.location.protocol}//${window.location.hostname}:80/talas5/ClientData/WebServiceMap.asmx` : `${window.location.origin}/talas5/ClientData/WebServiceMap.asmx`; + + const params = new URLSearchParams(window.location.search); + const idMap = params.get("m"); + + const url = `${baseUrl}/GisLinesStatus?idMap=${idMap}`; + console.log("📡 fetchGisLinesStatusService URL:", url); + + const response = await fetch(url); + if (!response.ok) throw new Error("Fehler beim Laden der Linienstatusdaten"); + + const text = await response.text(); + + let json; + try { + json = JSON.parse(text); + } catch (e) { + console.error("❌ Fehler beim JSON-Parsing der Antwort:", text); + throw new Error("Antwort ist kein gültiges JSON"); + } + + if (!Array.isArray(json.Statis)) throw new Error("Ungültige Antwortstruktur: Statis fehlt"); + return json.Statis; +}; diff --git a/utils/devices/createAndSetDevices.js b/utils/devices/createAndSetDevices.js index 62049d5c2..0f257afb7 100644 --- a/utils/devices/createAndSetDevices.js +++ b/utils/devices/createAndSetDevices.js @@ -7,8 +7,8 @@ import { store } from "../../redux/store.js"; import { updateLineStatus } from "../../redux/slices/lineVisibilitySlice.js"; import { setSelectedDevice, clearSelectedDevice } from "../../redux/slices/selectedDeviceSlice.js"; import { addContextMenuToMarker } from "../contextMenuUtils.js"; -import { selectGisStationsStaticDistrict } from "../../redux/slices/webService/gisStationsStaticDistrictSlice.js"; -import { selectGisStationsStatusDistrict } from "../../redux/slices/webService/gisStationsStatusDistrictSlice.js"; +import { selectGisStationsStaticDistrict } from "../../redux/slices/webservice/gisStationsStaticDistrictSlice.js"; +import { selectGisStationsStatusDistrict } from "../../redux/slices/webservice/gisStationsStatusDistrictSlice.js"; const determinePriority = (iconPath, priorityConfig) => { for (let priority of priorityConfig) {