From d8567a99288624e3f7bf3fb4b8bffc15fab1a8bd Mon Sep 17 00:00:00 2001 From: ISA Date: Tue, 29 Jul 2025 10:12:56 +0200 Subject: [PATCH] feat: implement map-specific localStorage and TALAS-Kabelstrecken dependency logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add map-specific localStorage keys using URL parameters (m=mapId, u=userId) - Implement kartenspezifische Sichtbarkeitseinstellungen per Map/User - Fix localStorage priority over GisSystemStatic Allow values to preserve user settings - Add bidirectional TALAS ↔ Kabelstrecken dependency logic: * Kabelstrecken aktiviert → TALAS automatisch aktiviert * TALAS deaktiviert → Kabelstrecken automatisch deaktiviert - Update mapLayersSlice.js to respect existing localStorage values over system defaults - Modify MapComponent.js to load map-specific visibility settings on mount - Update MapLayersControlPanel.js with kartenspezifische localStorage handling - Fix useDynamicDeviceLayers.js visibility logic (corrected boolean conditions) - Update useAreaMarkersLayer.js for map-specific localStorage keys BREAKING CHANGES: - localStorage structure changed from "mapLayersVisibility" to "mapLayersVisibility_m{mapId}_u{userId}" - User visibility preferences now have priority over GisSystemStatic Allow values - TALAS and Kabelstrecken are now logically linked (dependency relationship) This resolves issues with: - Map switching losing visibility settings - Browser reload overriding user preferences with system defaults - Missing logical connection between TALAS stations and their cable routes --- .env.development | 2 +- .env.production | 2 +- components/area/hooks/useAreaMarkersLayer.js | 11 ++- .../devices/hooks/useDynamicDeviceLayers.js | 20 ++++-- components/mainComponent/MapComponent.js | 39 +++++++++- .../MapLayersControlPanel.js | 71 ++++++++++++++----- package-lock.json | 4 +- package.json | 2 +- redux/slices/mapLayersSlice.js | 35 +++++++-- websocketDump/GisSystemStatic.json | 10 +-- 10 files changed, 158 insertions(+), 38 deletions(-) diff --git a/.env.development b/.env.development index 63bf2a1f9..85ec8a3ad 100644 --- a/.env.development +++ b/.env.development @@ -25,4 +25,4 @@ NEXT_PUBLIC_USE_MOCKS=true NEXT_PUBLIC_BASE_PATH=/talas5 # Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH= # App-Versionsnummer -NEXT_PUBLIC_APP_VERSION=1.1.306 +NEXT_PUBLIC_APP_VERSION=1.1.307 diff --git a/.env.production b/.env.production index 6f830332a..44a446fd8 100644 --- a/.env.production +++ b/.env.production @@ -26,4 +26,4 @@ NEXT_PUBLIC_BASE_PATH=/talas5 # Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH= # App-Versionsnummer -NEXT_PUBLIC_APP_VERSION=1.1.306 \ No newline at end of file +NEXT_PUBLIC_APP_VERSION=1.1.307 \ No newline at end of file diff --git a/components/area/hooks/useAreaMarkersLayer.js b/components/area/hooks/useAreaMarkersLayer.js index e295823a7..bf42cf60a 100644 --- a/components/area/hooks/useAreaMarkersLayer.js +++ b/components/area/hooks/useAreaMarkersLayer.js @@ -22,7 +22,13 @@ const useAreaMarkersLayer = (map, oms, apiUrl, onUpdateSuccess) => { const updateMarkersVisibility = () => { if (!map || areaMarkers.length === 0) return; - const mapLayersVisibility = JSON.parse(localStorage.getItem("mapLayersVisibility")) || {}; + // Kartenspezifischer localStorage-Key verwenden + const mapId = localStorage.getItem("currentMapId"); + const userId = localStorage.getItem("currentUserId"); + const mapStorageKey = + mapId && userId ? `mapLayersVisibility_m${mapId}_u${userId}` : "mapLayersVisibility"; + + const mapLayersVisibility = JSON.parse(localStorage.getItem(mapStorageKey)) || {}; const areAllLayersInvisible = Object.values(mapLayersVisibility).every(v => !v); if (areAllLayersInvisible === prevVisibility.current) return; @@ -42,7 +48,8 @@ const useAreaMarkersLayer = (map, oms, apiUrl, onUpdateSuccess) => { updateMarkersVisibility(); const handleStorageChange = event => { - if (event.key === "mapLayersVisibility") { + // Überwache sowohl den alten als auch kartenspezifische Keys + if (event.key === "mapLayersVisibility" || event.key?.startsWith("mapLayersVisibility_")) { updateMarkersVisibility(); } }; diff --git a/components/devices/hooks/useDynamicDeviceLayers.js b/components/devices/hooks/useDynamicDeviceLayers.js index 8b4198b06..c60612a8e 100644 --- a/components/devices/hooks/useDynamicDeviceLayers.js +++ b/components/devices/hooks/useDynamicDeviceLayers.js @@ -71,18 +71,28 @@ const useDynamicDeviceLayers = (map, GisSystemStatic, mapLayersVisibility, prior Object.entries(markerStates).forEach(([key, markers]) => { const isVisible = mapLayersVisibility[key]; + markers.forEach(marker => { const hasLayer = map.hasLayer(marker); - if (editMode || !isVisible) { - if (hasLayer) map.removeLayer(marker); - } else { - if (!hasLayer) marker.addTo(map); + + // Logik korrigiert: + // - Im editMode: alle Marker verstecken + // - Nicht im editMode: nur sichtbare Marker anzeigen (isVisible === true) + if (editMode || isVisible === false) { + // Marker verstecken + if (hasLayer) { + map.removeLayer(marker); + } + } else if (isVisible === true) { + // Marker anzeigen (nur wenn explizit true) + if (!hasLayer) { + marker.addTo(map); + } } }); }); const allMarkers = Object.values(markerStates).filter(Array.isArray).flat(); - checkOverlappingMarkers(map, allMarkers, plusRoundIcon); }, [map, markerStates, mapLayersVisibility]); diff --git a/components/mainComponent/MapComponent.js b/components/mainComponent/MapComponent.js index 3db1f0584..84e3c200f 100644 --- a/components/mainComponent/MapComponent.js +++ b/components/mainComponent/MapComponent.js @@ -35,7 +35,7 @@ import { useSelector, useDispatch } from "react-redux"; import { setSelectedPoi } from "@/redux/slices/database/pois/selectedPoiSlice.js"; import { setDisabled } from "@/redux/slices/database/polylines/polylineEventsDisabledSlice.js"; import { setMapId, setUserId } from "@/redux/slices/urlParameterSlice"; -import { selectMapLayersState } from "@/redux/slices/mapLayersSlice"; +import { selectMapLayersState, setLayerVisibility } from "@/redux/slices/mapLayersSlice"; import { setCurrentPoi } from "@/redux/slices/database/pois/currentPoiSlice.js"; import { selectGisLines } from "@/redux/slices/database/polylines/gisLinesSlice"; import { selectGisLinesStatus } from "@/redux/slices/webservice/gisLinesStatusSlice"; @@ -216,6 +216,42 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => { ); //-------------------------React Hooks-------------------------------- + + // URL-Parameter extrahieren und kartenspezifische localStorage-Keys verwenden + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const mapId = params.get("m"); + const userId = params.get("u"); + + if (mapId && userId) { + // Speichere aktuelle Map- und User-ID + localStorage.setItem("currentMapId", mapId); + localStorage.setItem("currentUserId", userId); + + // Kartenspezifischer localStorage-Key + const mapStorageKey = `mapLayersVisibility_m${mapId}_u${userId}`; + const storedMapLayersVisibility = localStorage.getItem(mapStorageKey); + + if (storedMapLayersVisibility) { + try { + const parsedVisibility = JSON.parse(storedMapLayersVisibility); + Object.keys(parsedVisibility).forEach(key => { + dispatch(setLayerVisibility({ layer: key, visibility: parsedVisibility[key] })); + }); + console.log( + `🔄 mapLayersVisibility für Map ${mapId}/User ${userId} geladen:`, + parsedVisibility + ); + } catch (error) { + console.error("❌ Fehler beim Laden von mapLayersVisibility:", error); + } + } else { + console.log( + `📝 Keine gespeicherten Einstellungen für Map ${mapId}/User ${userId} gefunden` + ); + } + } + }, []); // Nur einmal beim Mount ausführen useEffect(() => { useEffect(() => { if (linesData && Array.isArray(linesData)) { const transformed = linesData.map(item => ({ @@ -226,6 +262,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => { setLinePositions(transformed); } }, [linesData]); + //-------------------------------------------- useEffect(() => { dispatch(fetchPoiIconsDataThunk()); diff --git a/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js b/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js index 4fbc75747..549be769a 100644 --- a/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js +++ b/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js @@ -57,12 +57,21 @@ function MapLayersControlPanel() { console.log("📢 Event 'polylineVisibilityChanged' dispatched"); }, 100); + // Wenn Kabelstrecken aktiviert wird, aktiviere automatisch auch TALAS (IdSystem 1) if (checked) { - dispatch(setLayerVisibility({ layer: "TALAS", visibility: true })); - localStorage.setItem( - "mapLayersVisibility", - JSON.stringify({ ...mapLayersVisibility, TALAS: true }) - ); + const talasKey = "system-1"; // TALAS hat IdSystem 1 + dispatch(setLayerVisibility({ layer: talasKey, visibility: true })); + + // Kartenspezifischer localStorage-Key verwenden + const mapId = localStorage.getItem("currentMapId"); + const userId = localStorage.getItem("currentUserId"); + const mapStorageKey = + mapId && userId ? `mapLayersVisibility_m${mapId}_u${userId}` : "mapLayersVisibility"; + + const updatedVisibility = { ...mapLayersVisibility, [talasKey]: true }; + localStorage.setItem(mapStorageKey, JSON.stringify(updatedVisibility)); + + console.log("🔗 Kabelstrecken aktiviert → TALAS automatisch aktiviert"); } }; @@ -108,8 +117,15 @@ function MapLayersControlPanel() { }, 200); } - const storedMapLayersVisibility = localStorage.getItem("mapLayersVisibility"); - console.log("📦 MapLayers localStorage value:", storedMapLayersVisibility); + // Kartenspezifischer localStorage-Key verwenden + const mapId = localStorage.getItem("currentMapId"); + const userId = localStorage.getItem("currentUserId"); + const mapStorageKey = + mapId && userId ? `mapLayersVisibility_m${mapId}_u${userId}` : "mapLayersVisibility"; + + const storedMapLayersVisibility = localStorage.getItem(mapStorageKey); + console.log(`📦 MapLayers localStorage value for ${mapStorageKey}:`, storedMapLayersVisibility); + if (storedMapLayersVisibility) { const parsedVisibility = JSON.parse(storedMapLayersVisibility); Object.keys(parsedVisibility).forEach(key => { @@ -121,15 +137,16 @@ function MapLayersControlPanel() { if (Array.isArray(GisSystemStatic)) { const initialVisibility = {}; GisSystemStatic.forEach(system => { + const systemKey = `system-${system.IdSystem}`; const visibility = system.Allow === 1; - initialVisibility[system.SystemName] = visibility; - dispatch(setLayerVisibility({ layer: system.SystemName, visibility })); + initialVisibility[systemKey] = visibility; + dispatch(setLayerVisibility({ layer: systemKey, visibility })); console.log( - `🎯 Setting ${system.SystemName} visibility to ${visibility} (Allow=${system.Allow})` + `🎯 Setting ${systemKey} (${system.Name}) visibility to ${visibility} (Allow=${system.Allow})` ); }); - localStorage.setItem("mapLayersVisibility", JSON.stringify(initialVisibility)); - console.log("💾 Saved initial mapLayersVisibility to localStorage:", initialVisibility); + localStorage.setItem(mapStorageKey, JSON.stringify(initialVisibility)); + console.log(`💾 Saved initial mapLayersVisibility to ${mapStorageKey}:`, initialVisibility); } } @@ -197,10 +214,32 @@ function MapLayersControlPanel() { const { checked } = event.target; dispatch(setLayerVisibility({ layer: key, visibility: checked })); - localStorage.setItem( - "mapLayersVisibility", - JSON.stringify({ ...mapLayersVisibility, [key]: checked }) - ); + + // Kartenspezifischer localStorage-Key verwenden + const mapId = localStorage.getItem("currentMapId"); + const userId = localStorage.getItem("currentUserId"); + const mapStorageKey = + mapId && userId ? `mapLayersVisibility_m${mapId}_u${userId}` : "mapLayersVisibility"; + + localStorage.setItem(mapStorageKey, JSON.stringify({ ...mapLayersVisibility, [key]: checked })); + + // Wenn TALAS (system-1) deaktiviert wird, deaktiviere automatisch auch Kabelstrecken + if (key === "system-1" && !checked) { + console.log("🔗 TALAS deaktiviert → Kabelstrecken automatisch deaktiviert"); + + // Kabelstrecken deaktivieren + setKabelstreckenVisible(false); + localStorage.setItem("kabelstreckenVisible", "false"); + localStorage.setItem("polylineVisible", "false"); + dispatch(setPolylineVisible(false)); + + // Event für Kabelstrecken auslösen + setTimeout(() => { + const polylineEvent = new Event("polylineVisibilityChanged"); + window.dispatchEvent(polylineEvent); + console.log("📢 Event 'polylineVisibilityChanged' dispatched (auto-deactivated)"); + }, 50); + } setTimeout(() => { const event = new Event("visibilityChanged"); diff --git a/package-lock.json b/package-lock.json index 5d08177fb..7e1b6b93a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "nodemap", - "version": "1.1.306", + "version": "1.1.307", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "nodemap", - "version": "1.1.306", + "version": "1.1.307", "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", diff --git a/package.json b/package.json index 6e565c7fe..2129913ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nodemap", - "version": "1.1.306", + "version": "1.1.307", "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", diff --git a/redux/slices/mapLayersSlice.js b/redux/slices/mapLayersSlice.js index 686d3eacb..4b797e4d7 100644 --- a/redux/slices/mapLayersSlice.js +++ b/redux/slices/mapLayersSlice.js @@ -15,15 +15,42 @@ const mapLayersSlice = createSlice({ }, setLayerVisibility: (state, action) => { const { layer, visibility } = action.payload; - if (state[layer] !== undefined) { - state[layer] = visibility; - } + state[layer] = visibility; // Entferne die Bedingung, um sicherzustellen, dass Werte gesetzt werden }, setInitialLayers: (state, action) => { const systems = action.payload; // Array of GisSystem + + // Versuche kartenspezifische localStorage-Werte zu laden + const mapId = + typeof localStorage !== "undefined" ? localStorage.getItem("currentMapId") : null; + const userId = + typeof localStorage !== "undefined" ? localStorage.getItem("currentUserId") : null; + const mapStorageKey = + mapId && userId ? `mapLayersVisibility_m${mapId}_u${userId}` : "mapLayersVisibility"; + + let existingVisibility = {}; + if (typeof localStorage !== "undefined") { + try { + const stored = localStorage.getItem(mapStorageKey); + if (stored) { + existingVisibility = JSON.parse(stored); + } + } catch (error) { + console.error("Error loading stored visibility:", error); + } + } + systems.forEach(system => { const key = `system-${system.IdSystem}`; - state[key] = system.Allow === 1; // true wenn Allow=1, false wenn Allow=0 + + // Prüfe ob bereits ein localStorage-Wert existiert + if (existingVisibility.hasOwnProperty(key)) { + // Verwende gespeicherten Wert (Benutzer-Einstellung hat Priorität) + state[key] = existingVisibility[key]; + } else { + // Verwende Allow-Wert als Standard (nur für neue Systeme) + state[key] = system.Allow === 1; + } }); }, }, diff --git a/websocketDump/GisSystemStatic.json b/websocketDump/GisSystemStatic.json index 9aa0e2fd6..4a97c1281 100644 --- a/websocketDump/GisSystemStatic.json +++ b/websocketDump/GisSystemStatic.json @@ -3,35 +3,35 @@ "IdSystem": 1, "Name": "TALAS", "Longname": "Talas Meldestationen", - "Allow": 0, + "Allow": 1, "Icon": 1 }, { "IdSystem": 2, "Name": "ECI", "Longname": "ECI Geräte", - "Allow": 0, + "Allow": 1, "Icon": 2 }, { "IdSystem": 3, "Name": "ULAF", "Longname": "ULAF Geräte", - "Allow": 0, + "Allow": 1, "Icon": 3 }, { "IdSystem": 5, "Name": "GSM Modem", "Longname": "LR77 GSM Modems", - "Allow": 0, + "Allow": 1, "Icon": 5 }, { "IdSystem": 6, "Name": "Cisco Router", "Longname": "Cisco Router", - "Allow": 0, + "Allow": 1, "Icon": 6 }, {