feat: implement map-specific localStorage and TALAS-Kabelstrecken dependency logic

- 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
This commit is contained in:
ISA
2025-07-29 10:12:56 +02:00
parent 6d33be56c0
commit d8567a9928
10 changed files with 158 additions and 38 deletions

View File

@@ -25,4 +25,4 @@ NEXT_PUBLIC_USE_MOCKS=true
NEXT_PUBLIC_BASE_PATH=/talas5 NEXT_PUBLIC_BASE_PATH=/talas5
# Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH= # Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH=
# App-Versionsnummer # App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.1.306 NEXT_PUBLIC_APP_VERSION=1.1.307

View File

@@ -26,4 +26,4 @@ NEXT_PUBLIC_BASE_PATH=/talas5
# Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH= # Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH=
# App-Versionsnummer # App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.1.306 NEXT_PUBLIC_APP_VERSION=1.1.307

View File

@@ -22,7 +22,13 @@ const useAreaMarkersLayer = (map, oms, apiUrl, onUpdateSuccess) => {
const updateMarkersVisibility = () => { const updateMarkersVisibility = () => {
if (!map || areaMarkers.length === 0) return; 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); const areAllLayersInvisible = Object.values(mapLayersVisibility).every(v => !v);
if (areAllLayersInvisible === prevVisibility.current) return; if (areAllLayersInvisible === prevVisibility.current) return;
@@ -42,7 +48,8 @@ const useAreaMarkersLayer = (map, oms, apiUrl, onUpdateSuccess) => {
updateMarkersVisibility(); updateMarkersVisibility();
const handleStorageChange = event => { const handleStorageChange = event => {
if (event.key === "mapLayersVisibility") { // Überwache sowohl den alten als auch kartenspezifische Keys
if (event.key === "mapLayersVisibility" || event.key?.startsWith("mapLayersVisibility_")) {
updateMarkersVisibility(); updateMarkersVisibility();
} }
}; };

View File

@@ -71,18 +71,28 @@ const useDynamicDeviceLayers = (map, GisSystemStatic, mapLayersVisibility, prior
Object.entries(markerStates).forEach(([key, markers]) => { Object.entries(markerStates).forEach(([key, markers]) => {
const isVisible = mapLayersVisibility[key]; const isVisible = mapLayersVisibility[key];
markers.forEach(marker => { markers.forEach(marker => {
const hasLayer = map.hasLayer(marker); const hasLayer = map.hasLayer(marker);
if (editMode || !isVisible) {
if (hasLayer) map.removeLayer(marker); // Logik korrigiert:
} else { // - Im editMode: alle Marker verstecken
if (!hasLayer) marker.addTo(map); // - 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(); const allMarkers = Object.values(markerStates).filter(Array.isArray).flat();
checkOverlappingMarkers(map, allMarkers, plusRoundIcon); checkOverlappingMarkers(map, allMarkers, plusRoundIcon);
}, [map, markerStates, mapLayersVisibility]); }, [map, markerStates, mapLayersVisibility]);

View File

@@ -35,7 +35,7 @@ import { useSelector, useDispatch } from "react-redux";
import { setSelectedPoi } from "@/redux/slices/database/pois/selectedPoiSlice.js"; import { setSelectedPoi } from "@/redux/slices/database/pois/selectedPoiSlice.js";
import { setDisabled } from "@/redux/slices/database/polylines/polylineEventsDisabledSlice.js"; import { setDisabled } from "@/redux/slices/database/polylines/polylineEventsDisabledSlice.js";
import { setMapId, setUserId } from "@/redux/slices/urlParameterSlice"; 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 { setCurrentPoi } from "@/redux/slices/database/pois/currentPoiSlice.js";
import { selectGisLines } from "@/redux/slices/database/polylines/gisLinesSlice"; import { selectGisLines } from "@/redux/slices/database/polylines/gisLinesSlice";
import { selectGisLinesStatus } from "@/redux/slices/webservice/gisLinesStatusSlice"; import { selectGisLinesStatus } from "@/redux/slices/webservice/gisLinesStatusSlice";
@@ -216,6 +216,42 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
); );
//-------------------------React Hooks-------------------------------- //-------------------------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(() => { useEffect(() => {
if (linesData && Array.isArray(linesData)) { if (linesData && Array.isArray(linesData)) {
const transformed = linesData.map(item => ({ const transformed = linesData.map(item => ({
@@ -226,6 +262,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
setLinePositions(transformed); setLinePositions(transformed);
} }
}, [linesData]); }, [linesData]);
//-------------------------------------------- //--------------------------------------------
useEffect(() => { useEffect(() => {
dispatch(fetchPoiIconsDataThunk()); dispatch(fetchPoiIconsDataThunk());

View File

@@ -57,12 +57,21 @@ function MapLayersControlPanel() {
console.log("📢 Event 'polylineVisibilityChanged' dispatched"); console.log("📢 Event 'polylineVisibilityChanged' dispatched");
}, 100); }, 100);
// Wenn Kabelstrecken aktiviert wird, aktiviere automatisch auch TALAS (IdSystem 1)
if (checked) { if (checked) {
dispatch(setLayerVisibility({ layer: "TALAS", visibility: true })); const talasKey = "system-1"; // TALAS hat IdSystem 1
localStorage.setItem( dispatch(setLayerVisibility({ layer: talasKey, visibility: true }));
"mapLayersVisibility",
JSON.stringify({ ...mapLayersVisibility, TALAS: 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); }, 200);
} }
const storedMapLayersVisibility = localStorage.getItem("mapLayersVisibility"); // Kartenspezifischer localStorage-Key verwenden
console.log("📦 MapLayers localStorage value:", storedMapLayersVisibility); 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) { if (storedMapLayersVisibility) {
const parsedVisibility = JSON.parse(storedMapLayersVisibility); const parsedVisibility = JSON.parse(storedMapLayersVisibility);
Object.keys(parsedVisibility).forEach(key => { Object.keys(parsedVisibility).forEach(key => {
@@ -121,15 +137,16 @@ function MapLayersControlPanel() {
if (Array.isArray(GisSystemStatic)) { if (Array.isArray(GisSystemStatic)) {
const initialVisibility = {}; const initialVisibility = {};
GisSystemStatic.forEach(system => { GisSystemStatic.forEach(system => {
const systemKey = `system-${system.IdSystem}`;
const visibility = system.Allow === 1; const visibility = system.Allow === 1;
initialVisibility[system.SystemName] = visibility; initialVisibility[systemKey] = visibility;
dispatch(setLayerVisibility({ layer: system.SystemName, visibility })); dispatch(setLayerVisibility({ layer: systemKey, visibility }));
console.log( 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)); localStorage.setItem(mapStorageKey, JSON.stringify(initialVisibility));
console.log("💾 Saved initial mapLayersVisibility to localStorage:", initialVisibility); console.log(`💾 Saved initial mapLayersVisibility to ${mapStorageKey}:`, initialVisibility);
} }
} }
@@ -197,10 +214,32 @@ function MapLayersControlPanel() {
const { checked } = event.target; const { checked } = event.target;
dispatch(setLayerVisibility({ layer: key, visibility: checked })); dispatch(setLayerVisibility({ layer: key, visibility: checked }));
localStorage.setItem(
"mapLayersVisibility", // Kartenspezifischer localStorage-Key verwenden
JSON.stringify({ ...mapLayersVisibility, [key]: checked }) 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(() => { setTimeout(() => {
const event = new Event("visibilityChanged"); const event = new Event("visibilityChanged");

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "nodemap", "name": "nodemap",
"version": "1.1.306", "version": "1.1.307",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "nodemap", "name": "nodemap",
"version": "1.1.306", "version": "1.1.307",
"dependencies": { "dependencies": {
"@emotion/react": "^11.13.3", "@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0", "@emotion/styled": "^11.13.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "nodemap", "name": "nodemap",
"version": "1.1.306", "version": "1.1.307",
"dependencies": { "dependencies": {
"@emotion/react": "^11.13.3", "@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0", "@emotion/styled": "^11.13.0",

View File

@@ -15,15 +15,42 @@ const mapLayersSlice = createSlice({
}, },
setLayerVisibility: (state, action) => { setLayerVisibility: (state, action) => {
const { layer, visibility } = action.payload; const { layer, visibility } = action.payload;
if (state[layer] !== undefined) { state[layer] = visibility; // Entferne die Bedingung, um sicherzustellen, dass Werte gesetzt werden
state[layer] = visibility;
}
}, },
setInitialLayers: (state, action) => { setInitialLayers: (state, action) => {
const systems = action.payload; // Array of GisSystem 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 => { systems.forEach(system => {
const key = `system-${system.IdSystem}`; 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;
}
}); });
}, },
}, },

View File

@@ -3,35 +3,35 @@
"IdSystem": 1, "IdSystem": 1,
"Name": "TALAS", "Name": "TALAS",
"Longname": "Talas Meldestationen", "Longname": "Talas Meldestationen",
"Allow": 0, "Allow": 1,
"Icon": 1 "Icon": 1
}, },
{ {
"IdSystem": 2, "IdSystem": 2,
"Name": "ECI", "Name": "ECI",
"Longname": "ECI Geräte", "Longname": "ECI Geräte",
"Allow": 0, "Allow": 1,
"Icon": 2 "Icon": 2
}, },
{ {
"IdSystem": 3, "IdSystem": 3,
"Name": "ULAF", "Name": "ULAF",
"Longname": "ULAF Geräte", "Longname": "ULAF Geräte",
"Allow": 0, "Allow": 1,
"Icon": 3 "Icon": 3
}, },
{ {
"IdSystem": 5, "IdSystem": 5,
"Name": "GSM Modem", "Name": "GSM Modem",
"Longname": "LR77 GSM Modems", "Longname": "LR77 GSM Modems",
"Allow": 0, "Allow": 1,
"Icon": 5 "Icon": 5
}, },
{ {
"IdSystem": 6, "IdSystem": 6,
"Name": "Cisco Router", "Name": "Cisco Router",
"Longname": "Cisco Router", "Longname": "Cisco Router",
"Allow": 0, "Allow": 1,
"Icon": 6 "Icon": 6
}, },
{ {