Compare commits
21 Commits
3a9b436352
...
bfd091b1b1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfd091b1b1 | ||
|
|
81b6379895 | ||
|
|
42ca88d27e | ||
|
|
fdb70d892c | ||
|
|
73e9c63e36 | ||
|
|
e520207526 | ||
|
|
2e5acf9327 | ||
|
|
cdfdd3d6cf | ||
|
|
5b86d5293b | ||
|
|
31c770f778 | ||
|
|
051dd4c306 | ||
|
|
995f084e15 | ||
|
|
eaacec71da | ||
|
|
6bc2e16657 | ||
|
|
1208024f76 | ||
|
|
369f29a769 | ||
|
|
d166b2468d | ||
|
|
59c8680c23 | ||
|
|
1a046f8212 | ||
|
|
e35216daf5 | ||
|
|
91ad47166f |
@@ -23,4 +23,4 @@ NEXT_PUBLIC_USE_MOCKS=true
|
|||||||
# z.B. http://10.10.0.13/xyz/index.aspx -> basePath in config.json auf /xyz setzen
|
# z.B. http://10.10.0.13/xyz/index.aspx -> basePath in config.json auf /xyz setzen
|
||||||
# basePath wird jetzt in public/config.json gepflegt
|
# basePath wird jetzt in public/config.json gepflegt
|
||||||
# App-Versionsnummer
|
# App-Versionsnummer
|
||||||
NEXT_PUBLIC_APP_VERSION=1.1.361
|
NEXT_PUBLIC_APP_VERSION=1.1.382
|
||||||
|
|||||||
@@ -24,4 +24,4 @@ NEXT_PUBLIC_USE_MOCKS=false
|
|||||||
# basePath wird jetzt in public/config.json gepflegt
|
# basePath wird jetzt in public/config.json gepflegt
|
||||||
|
|
||||||
# App-Versionsnummer
|
# App-Versionsnummer
|
||||||
NEXT_PUBLIC_APP_VERSION=1.1.361
|
NEXT_PUBLIC_APP_VERSION=1.1.382
|
||||||
|
|||||||
22
.gitignore
vendored
@@ -35,3 +35,25 @@ docs.zip
|
|||||||
/mockData/
|
/mockData/
|
||||||
/__mocks__/
|
/__mocks__/
|
||||||
/__tests__/
|
/__tests__/
|
||||||
|
|
||||||
|
# --- Playwright artifacts & test selection ---
|
||||||
|
# Ignore Playwright output folders nested under playwright/
|
||||||
|
/playwright/test-results/
|
||||||
|
/playwright/playwright-report/
|
||||||
|
/playwright/.last-run.json
|
||||||
|
# If you ever enable these paths, keep them under playwright/ and ignore them
|
||||||
|
/playwright/traces/
|
||||||
|
/playwright/screenshots/
|
||||||
|
/playwright/videos/
|
||||||
|
# Ignore JUnit report artifacts under playwright/ (currently unused)
|
||||||
|
/playwright/reports/junit/
|
||||||
|
|
||||||
|
# Track only spec files under playwright/tests; ignore other files in that folder
|
||||||
|
/playwright/tests/**
|
||||||
|
!/playwright/tests/**/*.spec.js
|
||||||
|
!/playwright/tests/**/*.spec.ts
|
||||||
|
|
||||||
|
# Ignore Playwright cache if present
|
||||||
|
/playwright/.cache/
|
||||||
|
# playwright reports
|
||||||
|
/playwright/reports/
|
||||||
|
|||||||
18
components/icons/material-symbols/AlarmIcon.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const AlarmIcon = ({ className = "h-8 w-8" }) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="red"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
stroke="rgb(0, 174, 239)"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M6 6.9L3.87 4.78l1.41-1.41L7.4 5.5zM13 1v3h-2V1zm7.13 3.78L18 6.9l-1.4-1.4l2.12-2.13zM4.5 10.5v2h-3v-2zm15 0h3v2h-3zM6 20h12a2 2 0 0 1 2 2H4a2 2 0 0 1 2-2m6-15a6 6 0 0 1 6 6v8H6v-8a6 6 0 0 1 6-6"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default AlarmIcon;
|
||||||
18
components/icons/material-symbols/EditIcon.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const EditIcon = ({ className = "h-8 w-8" }) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
stroke="rgb(0, 174, 239)"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default EditIcon;
|
||||||
18
components/icons/material-symbols/EditOffIcon.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const EditOffIcon = ({ className = "h-8 w-8" }) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
stroke="rgb(0, 174, 239)"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L18 21l-3-3m-12.728-.364A9 9 0 015.636 5.636m0 0L3 3l3 3m9.364 9.364L18 21M5.636 5.636L3 3"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default EditOffIcon;
|
||||||
18
components/icons/material-symbols/ExpandIcon.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const ExpandIcon = ({ className = "h-8 w-8" }) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
stroke="rgb(0, 174, 239)"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default ExpandIcon;
|
||||||
18
components/icons/material-symbols/InfoIcon.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const InfoIcon = ({ className = "h-8 w-8" }) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
stroke="rgb(0, 174, 239)"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default InfoIcon;
|
||||||
19
components/icons/material-symbols/MapMarkerIcon.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
const MapMarkerIcon = ({ className = "h-8 w-8" }) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="rgb(0, 174, 239)"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
stroke="rgb(0, 174, 239)"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25s-7.5-4.108-7.5-11.25a7.5 7.5 0 1115 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default MapMarkerIcon;
|
||||||
18
components/icons/material-symbols/MenuIcon.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const MenuIcon = ({ className = "h-8 w-8" }) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
stroke="rgb(0, 174, 239)"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default MenuIcon;
|
||||||
14
components/icons/material-symbols/MinusIcon.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const MinusIcon = ({ className = "h-8 w-8" }) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M5 12h14" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default MinusIcon;
|
||||||
14
components/icons/material-symbols/PlusIcon.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const PlusIcon = ({ className = "h-8 w-8" }) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M12 5v14M5 12h14" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default PlusIcon;
|
||||||
18
components/icons/material-symbols/SearchIcon.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const SearchIcon = ({ className = "h-8 w-8" }) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
stroke="rgb(0, 174, 239)"
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default SearchIcon;
|
||||||
@@ -7,6 +7,16 @@ import "leaflet-contextmenu";
|
|||||||
import "leaflet.smooth_marker_bouncing";
|
import "leaflet.smooth_marker_bouncing";
|
||||||
import "react-toastify/dist/ReactToastify.css";
|
import "react-toastify/dist/ReactToastify.css";
|
||||||
import { Icon } from "@iconify/react";
|
import { Icon } from "@iconify/react";
|
||||||
|
import EditIcon from "@/components/icons/material-symbols/EditIcon";
|
||||||
|
import EditOffIcon from "@/components/icons/material-symbols/EditOffIcon";
|
||||||
|
import SearchIcon from "@/components/icons/material-symbols/SearchIcon";
|
||||||
|
import MenuIcon from "@/components/icons/material-symbols/MenuIcon";
|
||||||
|
import InfoIcon from "@/components/icons/material-symbols/InfoIcon";
|
||||||
|
import AlarmIcon from "@/components/icons/material-symbols/AlarmIcon";
|
||||||
|
import MapMarkerIcon from "@/components/icons/material-symbols/MapMarkerIcon";
|
||||||
|
import ExpandIcon from "@/components/icons/material-symbols/ExpandIcon";
|
||||||
|
import PlusIcon from "@/components/icons/material-symbols/PlusIcon";
|
||||||
|
import MinusIcon from "@/components/icons/material-symbols/MinusIcon";
|
||||||
import PoiUpdateModal from "@/components/pois/poiUpdateModal/PoiUpdateModal.js";
|
import PoiUpdateModal from "@/components/pois/poiUpdateModal/PoiUpdateModal.js";
|
||||||
import { ToastContainer, toast } from "react-toastify";
|
import { ToastContainer, toast } from "react-toastify";
|
||||||
import plusRoundIcon from "../icons/devices/overlapping/PlusRoundIcon.js";
|
import plusRoundIcon from "../icons/devices/overlapping/PlusRoundIcon.js";
|
||||||
@@ -24,9 +34,9 @@ import { useMapComponentState } from "@/components/hooks/useMapComponentState.js
|
|||||||
import CoordinatePopup from "@/components/contextmenu/CoordinatePopup.js";
|
import CoordinatePopup from "@/components/contextmenu/CoordinatePopup.js";
|
||||||
//----------Ui Widgets----------------
|
//----------Ui Widgets----------------
|
||||||
import MapLayersControlPanel from "@/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js";
|
import MapLayersControlPanel from "@/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js";
|
||||||
import BaseMapPanel from "@/components/uiWidgets/baseMapPanel/BaseMapPanel.js";
|
|
||||||
import CoordinateInput from "@/components/uiWidgets/CoordinateInput.js";
|
import CoordinateInput from "@/components/uiWidgets/CoordinateInput.js";
|
||||||
import VersionInfoModal from "@/components/uiWidgets/VersionInfoModal.js";
|
import VersionInfoModal from "@/components/uiWidgets/VersionInfoModal.js";
|
||||||
|
import AreaDropdown from "@/components/uiWidgets/AreaDropdown";
|
||||||
//----------Daten aus API--------------------
|
//----------Daten aus API--------------------
|
||||||
import { fetchPoiDataService } from "@/services/database/pois/fetchPoiDataByIdService.js";
|
import { fetchPoiDataService } from "@/services/database/pois/fetchPoiDataByIdService.js";
|
||||||
import AddPOIModal from "@/components/pois/AddPOIModal.js";
|
import AddPOIModal from "@/components/pois/AddPOIModal.js";
|
||||||
@@ -132,6 +142,13 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
const { data: gisLinesStatusData, status: statusGisLinesStatus } = useSelector(
|
const { data: gisLinesStatusData, status: statusGisLinesStatus } = useSelector(
|
||||||
selectGisLinesStatusFromWebservice
|
selectGisLinesStatusFromWebservice
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Alarm Status aus GisStationsStatusDistrict
|
||||||
|
const gisStationsStatusDistrict = useSelector(state => state.gisStationsStatusDistrict.data);
|
||||||
|
// Unterstützt sowohl Array-Shape (Statis[]) als auch Objekt mit Statis-Array
|
||||||
|
const hasActiveAlarm = Array.isArray(gisStationsStatusDistrict)
|
||||||
|
? gisStationsStatusDistrict.some(item => item?.Alarm === 1)
|
||||||
|
: gisStationsStatusDistrict?.Statis?.some(item => item?.Alarm === 1) || false;
|
||||||
const poiIconsData = useSelector(selectPoiIconsData);
|
const poiIconsData = useSelector(selectPoiIconsData);
|
||||||
const poiIconsStatus = useSelector(selectPoiIconsStatus);
|
const poiIconsStatus = useSelector(selectPoiIconsStatus);
|
||||||
const poiTypData = useSelector(selectPoiTypData);
|
const poiTypData = useSelector(selectPoiTypData);
|
||||||
@@ -150,6 +167,14 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
const [showVersionInfoModal, setShowVersionInfoModal] = useState(false);
|
const [showVersionInfoModal, setShowVersionInfoModal] = useState(false);
|
||||||
const [poiTypMap, setPoiTypMap] = useState(new Map());
|
const [poiTypMap, setPoiTypMap] = useState(new Map());
|
||||||
const [showPopup, setShowPopup] = useState(false);
|
const [showPopup, setShowPopup] = useState(false);
|
||||||
|
const [showAreaDropdown, setShowAreaDropdown] = useState(() => {
|
||||||
|
try {
|
||||||
|
const v = localStorage.getItem("showAreaDropdown");
|
||||||
|
return v === null ? false : v === "true";
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
const poiLayerRef = useRef(null); // Referenz auf die Layer-Gruppe für Datenbank-Marker
|
const poiLayerRef = useRef(null); // Referenz auf die Layer-Gruppe für Datenbank-Marker
|
||||||
const mapRef = useRef(null); // Referenz auf das DIV-Element der Karte
|
const mapRef = useRef(null); // Referenz auf das DIV-Element der Karte
|
||||||
const [map, setMap] = useState(null); // Zustand der Karteninstanz
|
const [map, setMap] = useState(null); // Zustand der Karteninstanz
|
||||||
@@ -172,15 +197,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Sichtbarkeit des Base-Map Panels (oben rechts, unter Toolbar)
|
// Base-Map Panel wurde entfernt
|
||||||
const [showBaseMapPanel, setShowBaseMapPanel] = useState(() => {
|
|
||||||
try {
|
|
||||||
const v = localStorage.getItem("showBaseMapPanel");
|
|
||||||
return v === null ? false : v === "true";
|
|
||||||
} catch (_) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Sichtbarkeit der Koordinaten-Suche (Lupe)
|
// Sichtbarkeit der Koordinaten-Suche (Lupe)
|
||||||
const [showCoordinateInput, setShowCoordinateInput] = useState(() => {
|
const [showCoordinateInput, setShowCoordinateInput] = useState(() => {
|
||||||
try {
|
try {
|
||||||
@@ -191,6 +208,29 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Zentrale Steuerung: Nur ein Overlay gleichzeitig
|
||||||
|
// Mögliche Werte: null | 'area' | 'layers' | 'coord' | 'info'
|
||||||
|
const [overlay, setOverlay] = useState(null);
|
||||||
|
|
||||||
|
// Initiale Bestimmung des aktiven Overlays basierend auf bestehenden Flags
|
||||||
|
useEffect(() => {
|
||||||
|
if (showAreaDropdown) setOverlay("area");
|
||||||
|
else if (showLayersPanel) setOverlay("layers");
|
||||||
|
else if (showCoordinateInput) setOverlay("coord");
|
||||||
|
else if (showAppInfoCard) setOverlay("info");
|
||||||
|
else setOverlay(null);
|
||||||
|
// nur beim Mount ausführen
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Flags mit Overlay-State synchronisieren (persistiert weiterhin in bestehenden Effects)
|
||||||
|
useEffect(() => {
|
||||||
|
setShowAreaDropdown(overlay === "area");
|
||||||
|
setShowLayersPanel(overlay === "layers");
|
||||||
|
setShowCoordinateInput(overlay === "coord");
|
||||||
|
setShowAppInfoCard(overlay === "info");
|
||||||
|
}, [overlay]);
|
||||||
|
|
||||||
// Flag, ob Nutzer die Polyline-Checkbox manuell betätigt hat
|
// Flag, ob Nutzer die Polyline-Checkbox manuell betätigt hat
|
||||||
// Nutzer-Flag global auf window, damit auch Redux darauf zugreifen kann
|
// Nutzer-Flag global auf window, damit auch Redux darauf zugreifen kann
|
||||||
if (typeof window !== "undefined" && window.userToggledPolyline === undefined) {
|
if (typeof window !== "undefined" && window.userToggledPolyline === undefined) {
|
||||||
@@ -262,18 +302,19 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
localStorage.setItem("showAppInfoCard", String(showAppInfoCard));
|
localStorage.setItem("showAppInfoCard", String(showAppInfoCard));
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}, [showAppInfoCard]);
|
}, [showAppInfoCard]);
|
||||||
|
// Persistiere Sichtbarkeit des Area-Dropdowns (Marker-Overlay)
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem("showAreaDropdown", String(showAreaDropdown));
|
||||||
|
} catch (_) {}
|
||||||
|
}, [showAreaDropdown]);
|
||||||
// Persistiere Sichtbarkeit des Layer-Panels
|
// Persistiere Sichtbarkeit des Layer-Panels
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
localStorage.setItem("showLayersPanel", String(showLayersPanel));
|
localStorage.setItem("showLayersPanel", String(showLayersPanel));
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}, [showLayersPanel]);
|
}, [showLayersPanel]);
|
||||||
// Persistiere Sichtbarkeit des Base-Map Panels
|
// Persist-Logik für Base-Map Panel entfernt
|
||||||
useEffect(() => {
|
|
||||||
try {
|
|
||||||
localStorage.setItem("showBaseMapPanel", String(showBaseMapPanel));
|
|
||||||
} catch (_) {}
|
|
||||||
}, [showBaseMapPanel]);
|
|
||||||
// Persistiere Sichtbarkeit der Koordinaten-Suche
|
// Persistiere Sichtbarkeit der Koordinaten-Suche
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
@@ -1132,7 +1173,8 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
|
|
||||||
{GisStationsStaticDistrict &&
|
{GisStationsStaticDistrict &&
|
||||||
GisStationsStaticDistrict.Points?.length > 0 &&
|
GisStationsStaticDistrict.Points?.length > 0 &&
|
||||||
showLayersPanel && (
|
showLayersPanel &&
|
||||||
|
!showAreaDropdown && (
|
||||||
<MapLayersControlPanel
|
<MapLayersControlPanel
|
||||||
className="z-50"
|
className="z-50"
|
||||||
handlePolylineCheckboxChange={handlePolylineCheckboxChange}
|
handlePolylineCheckboxChange={handlePolylineCheckboxChange}
|
||||||
@@ -1143,6 +1185,39 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
<div id="map" ref={mapRef} className="z-0" style={{ height: "100vh", width: "100vw" }}></div>
|
<div id="map" ref={mapRef} className="z-0" style={{ height: "100vh", width: "100vw" }}></div>
|
||||||
{/* Top-right controls: layers, info, expand, edit, and base map stack */}
|
{/* Top-right controls: layers, info, expand, edit, and base map stack */}
|
||||||
<div className="absolute top-3 right-3 z-50 pointer-events-auto flex items-center gap-2">
|
<div className="absolute top-3 right-3 z-50 pointer-events-auto flex items-center gap-2">
|
||||||
|
{/* Alarm-Icon - nur anzeigen wenn Alarm aktiv */}
|
||||||
|
{hasActiveAlarm && (
|
||||||
|
<button
|
||||||
|
onClick={() => {}}
|
||||||
|
aria-label="Alarm aktiv"
|
||||||
|
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||||
|
title="Alarm aktiv"
|
||||||
|
>
|
||||||
|
<AlarmIcon className="h-8 w-8 animate-pulse text-red-500" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{/* Marker-Icon (line-md) */}
|
||||||
|
<button
|
||||||
|
onClick={() => setOverlay(prev => (prev === "area" ? null : "area"))}
|
||||||
|
aria-label="Marker"
|
||||||
|
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||||
|
title="Marker"
|
||||||
|
>
|
||||||
|
<MapMarkerIcon className="h-8 w-8" />
|
||||||
|
</button>
|
||||||
|
{/*Lupe: Koordinatensuche ein-/ausblenden */}
|
||||||
|
<button
|
||||||
|
onClick={() => setOverlay(prev => (prev === "coord" ? null : "coord"))}
|
||||||
|
aria-label={
|
||||||
|
showCoordinateInput ? "Koordinatensuche ausblenden" : "Koordinatensuche einblenden"
|
||||||
|
}
|
||||||
|
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||||
|
title={
|
||||||
|
showCoordinateInput ? "Koordinatensuche ausblenden" : "Koordinatensuche einblenden"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SearchIcon className="h-8 w-8" />
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={toggleEditMode}
|
onClick={toggleEditMode}
|
||||||
aria-label={editMode ? "Bearbeitungsmodus deaktivieren" : "Bearbeitungsmodus aktivieren"}
|
aria-label={editMode ? "Bearbeitungsmodus deaktivieren" : "Bearbeitungsmodus aktivieren"}
|
||||||
@@ -1160,88 +1235,67 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
}
|
}
|
||||||
disabled={!hasEditRight}
|
disabled={!hasEditRight}
|
||||||
>
|
>
|
||||||
<Icon
|
{editMode ? <EditOffIcon className="h-8 w-8" /> : <EditIcon className="h-8 w-8" />}
|
||||||
icon={editMode ? "material-symbols:edit-off-rounded" : "material-symbols:edit-rounded"}
|
|
||||||
className="h-8 w-8 text-blue-900"
|
|
||||||
/>
|
|
||||||
</button>
|
</button>
|
||||||
|
{/* Expand: Karte auf Standardansicht */}
|
||||||
<button
|
<button
|
||||||
onClick={handleExpandClick}
|
onClick={handleExpandClick}
|
||||||
aria-label="Karte auf Standardansicht"
|
aria-label="Karte auf Standardansicht"
|
||||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
className="rounded-full bg-white/90 hover:bg-white shadow p-1 "
|
||||||
title="Karte auf Standardansicht"
|
title="Karte auf Standardansicht"
|
||||||
>
|
>
|
||||||
<img src="/img/expand-icon.svg" alt="Expand" className="h-8 w-8" />
|
<ExpandIcon className="h-8 w-8" />
|
||||||
</button>
|
</button>
|
||||||
|
{/* Lupe: Koordinaten-Suche ein-/ausblenden */}
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowBaseMapPanel(v => !v)}
|
onClick={() => setOverlay(prev => (prev === "layers" ? null : "layers"))}
|
||||||
aria-label={
|
|
||||||
showBaseMapPanel ? "Kartenhintergrund ausblenden" : "Kartenhintergrund wählen"
|
|
||||||
}
|
|
||||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
|
||||||
title={showBaseMapPanel ? "Kartenhintergrund ausblenden" : "Kartenhintergrund wählen"}
|
|
||||||
>
|
|
||||||
<Icon icon="material-symbols:layers-rounded" className="h-8 w-8 text-blue-900" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setShowLayersPanel(v => !v)}
|
|
||||||
aria-label={showLayersPanel ? "Layer-Panel ausblenden" : "Layer-Panel einblenden"}
|
aria-label={showLayersPanel ? "Layer-Panel ausblenden" : "Layer-Panel einblenden"}
|
||||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||||
title={showLayersPanel ? "Layer-Panel ausblenden" : "Layer-Panel einblenden"}
|
title={showLayersPanel ? "Layer-Panel ausblenden" : "Layer-Panel einblenden"}
|
||||||
>
|
>
|
||||||
<Icon icon="material-symbols:menu-rounded" className="h-8 w-8 text-blue-900" />
|
<MenuIcon className="h-8 w-8" />
|
||||||
</button>
|
</button>
|
||||||
{/* Lupe: Koordinaten-Suche ein-/ausblenden */}
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowCoordinateInput(v => !v)}
|
onClick={() => setOverlay(prev => (prev === "info" ? null : "info"))}
|
||||||
aria-label={
|
|
||||||
showCoordinateInput ? "Koordinatensuche ausblenden" : "Koordinatensuche einblenden"
|
|
||||||
}
|
|
||||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
|
||||||
title={
|
|
||||||
showCoordinateInput ? "Koordinatensuche ausblenden" : "Koordinatensuche einblenden"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Icon icon="material-symbols:search-rounded" className="h-8 w-8 text-blue-900" />
|
|
||||||
</button>
|
|
||||||
{/* Marker-Icon (line-md) */}
|
|
||||||
<button
|
|
||||||
onClick={() => {}}
|
|
||||||
aria-label="Marker"
|
|
||||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
|
||||||
title="Marker"
|
|
||||||
>
|
|
||||||
<Icon icon="line-md:map-marker-filled" className="h-8 w-8 text-blue-900" />
|
|
||||||
</button>
|
|
||||||
{/* Alarm-Icon (mdi) */}
|
|
||||||
<button
|
|
||||||
onClick={() => {}}
|
|
||||||
aria-label="Alarm"
|
|
||||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
|
||||||
title="Alarm"
|
|
||||||
>
|
|
||||||
<Icon icon="mdi:alarm-light-outline" className="h-8 w-8 text-blue-900" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setShowAppInfoCard(v => !v)}
|
|
||||||
aria-label={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
|
aria-label={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
|
||||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||||
title={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
|
title={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
|
||||||
>
|
>
|
||||||
<Icon
|
<InfoIcon
|
||||||
icon="material-symbols:info-rounded"
|
className="h-8 w-8 pr-1"
|
||||||
className="text-blue-900 h-8 w-8 pr-1"
|
|
||||||
title={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
|
title={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{showBaseMapPanel && map && (
|
{/* Custom Zoom Controls bottom-right, styled in littwin-blue to match app icons */}
|
||||||
<BaseMapPanel map={map} onClose={() => setShowBaseMapPanel(false)} />
|
<div className="absolute bottom-8 right-3 z-50 flex flex-col gap-1">
|
||||||
)}
|
<button
|
||||||
|
data-testid="zoom-in"
|
||||||
|
onClick={() => map?.zoomIn?.()}
|
||||||
|
aria-label="Zoom in"
|
||||||
|
className="rounded-md bg-white/90 hover:bg-white shadow-sm p-1"
|
||||||
|
title="Zoom in"
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-5 w-5 text-littwin-blue" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
data-testid="zoom-out"
|
||||||
|
onClick={() => map?.zoomOut?.()}
|
||||||
|
aria-label="Zoom out"
|
||||||
|
className="rounded-md bg-white/90 hover:bg-white shadow-sm p-1"
|
||||||
|
title="Zoom out"
|
||||||
|
>
|
||||||
|
<MinusIcon className="h-5 w-5 text-littwin-blue" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/* Marker/AreaDropdown Panel außerhalb der Button-Leiste platzieren, damit die Position mit den anderen Panels identisch ist */}
|
||||||
|
{overlay === "area" && <AreaDropdown onClose={() => setOverlay(null)} />}
|
||||||
|
{/* BaseMapPanel entfernt */}
|
||||||
<CoordinatePopup isOpen={isPopupOpen} coordinates={currentCoordinates} onClose={closePopup} />
|
<CoordinatePopup isOpen={isPopupOpen} coordinates={currentCoordinates} onClose={closePopup} />
|
||||||
|
|
||||||
{showAppInfoCard && (
|
{showAppInfoCard && (
|
||||||
<div className="absolute bottom-3 left-3 w-72 p-4 bg-white rounded-lg shadow-md z-50">
|
<div className="absolute top-16 right-3 w-72 p-4 bg-white rounded-lg shadow-md z-50">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div>
|
<div>
|
||||||
<span className="text-black text-lg font-semibold"> TALAS.Map </span>
|
<span className="text-black text-lg font-semibold"> TALAS.Map </span>
|
||||||
@@ -1250,11 +1304,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button onClick={openVersionInfoModal}>
|
<button onClick={openVersionInfoModal}>
|
||||||
<Icon
|
<InfoIcon className="h-8 w-8 pr-1" title="Weitere Infos" />
|
||||||
icon="material-symbols:info-rounded"
|
|
||||||
className="text-blue-900 h-8 w-8 pr-1"
|
|
||||||
title="Weitere Infos"
|
|
||||||
/>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
79
components/uiWidgets/AreaDropdown.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// /components/uiWidgets/AreaDropdown.js
|
||||||
|
import React, { useEffect, useMemo } from "react";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { setSelectedArea } from "@/redux/slices/selectedAreaSlice";
|
||||||
|
import { selectGisStationsStaticDistrict } from "@/redux/slices/webservice/gisStationsStaticDistrictSlice";
|
||||||
|
import { selectGisSystemStatic } from "@/redux/slices/webservice/gisSystemStaticSlice";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kleines Dropdown zur Auswahl der Station (Area_Name),
|
||||||
|
* nutzt dieselbe Datenquelle wie das MapLayersControlPanel.
|
||||||
|
*/
|
||||||
|
const AreaDropdown = ({ onClose }) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const GisStationsStaticDistrict = useSelector(selectGisStationsStaticDistrict) || {};
|
||||||
|
const GisSystemStatic = useSelector(selectGisSystemStatic) || [];
|
||||||
|
|
||||||
|
// Erlaubte Systeme: Allow === 1 und Map === 1
|
||||||
|
const allowedSystems = useMemo(() => {
|
||||||
|
return new Set(
|
||||||
|
(Array.isArray(GisSystemStatic) ? GisSystemStatic : [])
|
||||||
|
.filter(sys => sys.Allow === 1 && sys.Map === 1)
|
||||||
|
.map(sys => sys.IdSystem)
|
||||||
|
);
|
||||||
|
}, [GisSystemStatic]);
|
||||||
|
|
||||||
|
// Uniqe Areas basierend auf Allowed Systems
|
||||||
|
const areaOptions = useMemo(() => {
|
||||||
|
const points = GisStationsStaticDistrict?.Points || [];
|
||||||
|
const seen = new Set();
|
||||||
|
const filtered = points.filter(p => {
|
||||||
|
if (!p?.Area_Name) return false;
|
||||||
|
if (!allowedSystems.has(p.System)) return false;
|
||||||
|
if (seen.has(p.Area_Name)) return false;
|
||||||
|
seen.add(p.Area_Name);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return filtered.map(p => ({ label: p.Area_Name, value: p.IdLD }));
|
||||||
|
}, [GisStationsStaticDistrict, allowedSystems]);
|
||||||
|
|
||||||
|
const handleChange = e => {
|
||||||
|
const selectedIndex = e.target.options.selectedIndex;
|
||||||
|
const label = e.target.options[selectedIndex].text;
|
||||||
|
dispatch(setSelectedArea(label));
|
||||||
|
onClose?.();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Schließe mit ESC
|
||||||
|
useEffect(() => {
|
||||||
|
const onKey = e => {
|
||||||
|
if (e.key === "Escape") onClose?.();
|
||||||
|
};
|
||||||
|
window.addEventListener("keydown", onKey);
|
||||||
|
return () => window.removeEventListener("keydown", onKey);
|
||||||
|
}, [onClose]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="absolute top-16 right-3 w-72 z-50 bg-white rounded-lg shadow-md">
|
||||||
|
<div className="flex flex-col gap-4 p-4">
|
||||||
|
<div className="text-sm font-semibold mb-2">Station wählen</div>
|
||||||
|
<select
|
||||||
|
onChange={handleChange}
|
||||||
|
className="border p-2 rounded w-full"
|
||||||
|
defaultValue="__default__"
|
||||||
|
>
|
||||||
|
<option value="__default__" disabled>
|
||||||
|
Bitte wählen…
|
||||||
|
</option>
|
||||||
|
{areaOptions.map(opt => (
|
||||||
|
<option key={opt.value} value={opt.value}>
|
||||||
|
{opt.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AreaDropdown;
|
||||||
@@ -4,7 +4,7 @@ import React, { useState } from "react";
|
|||||||
const CoordinateInput = ({ onCoordinatesSubmit }) => {
|
const CoordinateInput = ({ onCoordinatesSubmit }) => {
|
||||||
const [coordinates, setCoordinates] = useState("");
|
const [coordinates, setCoordinates] = useState("");
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (onCoordinatesSubmit) {
|
if (onCoordinatesSubmit) {
|
||||||
onCoordinatesSubmit(coordinates);
|
onCoordinatesSubmit(coordinates);
|
||||||
@@ -12,9 +12,21 @@ const CoordinateInput = ({ onCoordinatesSubmit }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit} className="fixed top-5 left-5 z-50 bg-white shadow-lg rounded-lg p-4 w-72">
|
<form
|
||||||
<input type="text" placeholder="Koordinaten eingeben (lat,lng)" value={coordinates} onChange={(e) => setCoordinates(e.target.value)} className="border p-2 rounded w-full mb-2" />
|
onSubmit={handleSubmit}
|
||||||
<button type="submit" className="bg-blue-500 text-white p-2 rounded w-full hover:bg-blue-600">
|
className="absolute top-16 right-3 z-50 bg-white rounded-lg shadow-md p-4 w-72"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Koordinaten eingeben (lat,lng)"
|
||||||
|
value={coordinates}
|
||||||
|
onChange={e => setCoordinates(e.target.value)}
|
||||||
|
className="border p-2 rounded w-full mb-2"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="bg-littwin-blue text-white p-2 rounded w-full hover:bg-blue-600"
|
||||||
|
>
|
||||||
Zu Marker zoomen
|
Zu Marker zoomen
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const VersionInfoModal = ({ showVersionInfoModal, closeVersionInfoModal, APP_VER
|
|||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={closeVersionInfoModal}
|
onClick={closeVersionInfoModal}
|
||||||
className="mt-4 bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-700 mx-auto block"
|
className="mt-4 bg-littwin-blue text-white px-4 py-2 rounded mx-auto block"
|
||||||
>
|
>
|
||||||
Schließen
|
Schließen
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// components/uiWidgets/baseMapPanel/BaseMapPanel.js
|
// components/uiWidgets/baseMapPanel/BaseMapPanel.js , aus rechliche Grunde nur OSM, dieses Feature ist optional, aktuell nicht genutzt
|
||||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import L from "leaflet";
|
import L from "leaflet";
|
||||||
import { Icon } from "@iconify/react";
|
import { Icon } from "@iconify/react";
|
||||||
|
|||||||
@@ -241,53 +241,42 @@ function MapLayersControlPanel({ handlePolylineCheckboxChange }) {
|
|||||||
}, [GisStationsStaticDistrict]);
|
}, [GisStationsStaticDistrict]);
|
||||||
//---------------------------
|
//---------------------------
|
||||||
|
|
||||||
|
// Polyline (Kabelstrecken) abhängig von TALAS (system-1)
|
||||||
|
const onPolylineToggle = checked => {
|
||||||
|
if (editMode) return;
|
||||||
|
|
||||||
|
// Wenn Nutzer Kabelstrecken einschaltet, aber TALAS aktuell ausgeblendet ist,
|
||||||
|
// dann TALAS automatisch aktivieren (sofern erlaubt)
|
||||||
|
const talasKey = "system-1";
|
||||||
|
const talasVisible = !!mapLayersVisibility[talasKey];
|
||||||
|
if (checked && isTalasAllowed && !talasVisible) {
|
||||||
|
dispatch(setLayerVisibility({ layer: talasKey, visibility: true }));
|
||||||
|
|
||||||
|
// Persistiere Sichtbarkeit map/user-spezifisch
|
||||||
|
const mapId2 = localStorage.getItem("currentMapId");
|
||||||
|
const userId2 = localStorage.getItem("currentUserId");
|
||||||
|
const mapStorageKey =
|
||||||
|
mapId2 && userId2 ? `mapLayersVisibility_m${mapId2}_u${userId2}` : "mapLayersVisibility";
|
||||||
|
localStorage.setItem(
|
||||||
|
mapStorageKey,
|
||||||
|
JSON.stringify({ ...mapLayersVisibility, [talasKey]: true })
|
||||||
|
);
|
||||||
|
|
||||||
|
// Event feuern wie an anderer Stelle
|
||||||
|
setTimeout(() => {
|
||||||
|
const event = new Event("visibilityChanged");
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sichtbarkeit der Kabelstrecken setzen
|
||||||
|
handlePolylineCheckboxChange(checked);
|
||||||
|
};
|
||||||
|
|
||||||
//---------------------------
|
//---------------------------
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="absolute top-16 right-3 w-72 z-50 bg-white rounded-lg shadow-md">
|
||||||
id="mainDataSheet"
|
<div id="mainDataSheet" className="flex flex-col gap-4 p-4">
|
||||||
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 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>
|
|
||||||
{/*
|
|
||||||
...new Map(
|
|
||||||
(GisStationsStaticDistrict.Points || [])
|
|
||||||
.filter(p => !!p.Area_Name)
|
|
||||||
.map(p => [p.Area_Name, p])
|
|
||||||
).values(),
|
|
||||||
*/}
|
|
||||||
{[
|
|
||||||
...new Map(
|
|
||||||
(GisStationsStaticDistrict.Points || [])
|
|
||||||
.filter(p => !!p.Area_Name)
|
|
||||||
.map(p => [p.Area_Name, p])
|
|
||||||
).values(),
|
|
||||||
].map(item => (
|
|
||||||
<option key={item.Area_Name} value={item.IdLD}>
|
|
||||||
{item.Area_Name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
{/*
|
|
||||||
<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>
|
|
||||||
|
|
||||||
{/* Checkboxen mit Untermenüs */}
|
{/* Checkboxen mit Untermenüs */}
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{systemListing.map(system => (
|
{systemListing.map(system => (
|
||||||
@@ -312,7 +301,7 @@ function MapLayersControlPanel({ handlePolylineCheckboxChange }) {
|
|||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={kabelstreckenVisible}
|
checked={kabelstreckenVisible}
|
||||||
onChange={e => handlePolylineCheckboxChange(e.target.checked)}
|
onChange={e => onPolylineToggle(e.target.checked)}
|
||||||
id="polyline-checkbox"
|
id="polyline-checkbox"
|
||||||
disabled={!isTalasAllowed || editMode}
|
disabled={!isTalasAllowed || editMode}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -22,4 +22,73 @@ Verzeichnisstruktur funktioniert.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## OSM‑basierte, „open“ Quellen
|
||||||
|
|
||||||
|
Diese sind datenrechtlich offen (ODbL bzw. Community-Lizenzen), aber das „kostenlos“ gilt nicht im
|
||||||
|
Sinne unbegrenzter Tile‑Nutzung. Die Tile‑Server werden als Community‑Ressource bereitgestellt –
|
||||||
|
bitte Policies respektieren.
|
||||||
|
|
||||||
|
- osm-standard (OpenStreetMap)
|
||||||
|
- - Key: Nein
|
||||||
|
- - Nutzung: Fair‑Use; für produktive/hohe Last eigenen Tile‑Server/Provider verwenden.
|
||||||
|
- - Attribution: „© OpenStreetMap contributors“
|
||||||
|
|
||||||
|
- osm-humanitarian (HOT)
|
||||||
|
- - Key: Nein
|
||||||
|
- - Nutzung: Fair‑Use; für größere Last die Betreiber kontaktieren bzw. andere Infrastruktur nutzen.
|
||||||
|
- - Attribution: „© OpenStreetMap contributors <br>
|
||||||
|
-
|
||||||
|
- Humanitarian OpenStreetMap Team“ cyclosm
|
||||||
|
- - Key: Nein
|
||||||
|
- - Nutzung: Fair‑Use (bereitgestellt u. a. über OSM France). Für höhere Last
|
||||||
|
Unterstützung/Hostingoptionen prüfen.
|
||||||
|
- - Attribution: „CyclOSM“ + „OpenStreetMap contributors“
|
||||||
|
- Praxis‑Tipps Kleine bis mittlere Nutzung: Die oben genannten „keyless“ Quellen sind oft
|
||||||
|
ausreichend, solange du Attribution setzt und Limits respektierst. Produktion/hohe Last: Nutze
|
||||||
|
einen Anbieter mit Vertrag/Key (z. B. Thunderforest, Tracestrack, MapTiler, Mapbox) oder hoste
|
||||||
|
Tiles selbst. Schlüssel im Client: Für Thunderforest/Tracestrack stehen die Keys im Frontend. Das
|
||||||
|
ist üblich, aber der Key ist sichtbar. Wenn du ihn verbergen willst, richte einen kleinen
|
||||||
|
Tile‑Proxy auf deinem Server ein, der den Key serverseitig anhängt und optional cached.
|
||||||
|
Attribution: Dein BaseMapPanel setzt bereits Attributionsstrings aus config.json. Achte darauf,
|
||||||
|
dass sie je Quelle korrekt sind.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Kurzantwort: Für kommerzielle Nutzung sind OSM‑Community‑Tile‑Server nicht geeignet. Nutze einen
|
||||||
|
bezahlten Anbieter (z. B. Thunderforest, Tracestrack) oder hoste selbst. Attribution ist immer
|
||||||
|
Pflicht.
|
||||||
|
|
||||||
|
Links und Hinweise je Layer/Provider:
|
||||||
|
|
||||||
|
OpenStreetMap Standard (osm-standard)
|
||||||
|
|
||||||
|
Lizenz/Daten: ODbL, Attribution Pflicht Tile-Server-Policy (keine Produktion/hohe Last):
|
||||||
|
https://operations.osmfoundation.org/policies/tiles/ Urheberrecht/Attribution:
|
||||||
|
https://www.openstreetmap.org/copyright HOT Humanitarian (osm-humanitarian)
|
||||||
|
|
||||||
|
Community-Server (OSM France); keine Produktion/hohe Last Info/Policy OSM France Tiles:
|
||||||
|
https://tile.openstreetmap.fr/ HOT: https://www.hotosm.org/ CyclOSM (cyclosm)
|
||||||
|
|
||||||
|
Community-Server (OSM France); keine Produktion/hohe Last Projektseite: https://www.cyclosm.org/
|
||||||
|
Hinweise/Policy (OSM France): https://tile.openstreetmap.fr/ Wiki:
|
||||||
|
https://wiki.openstreetmap.org/wiki/CyclOSM Carto Light (carto-light / Positron)
|
||||||
|
|
||||||
|
Keylos nutzbar mit Attribution; Fair‑Use, für hohe Last über CARTO‑Pläne Basemaps:
|
||||||
|
https://carto.com/basemaps/ Attribution: https://carto.com/attributions Pricing (Plattform):
|
||||||
|
https://carto.com/pricing/ (bei großem Volumen Sales kontaktieren) Thunderforest (Cycle/Transport u.
|
||||||
|
a.)
|
||||||
|
|
||||||
|
Kommerziell mit API‑Key; Pläne von Free bis Pro Pricing: https://www.thunderforest.com/pricing/
|
||||||
|
Terms/Attribution: https://www.thunderforest.com/terms/ Tracestrack Topo
|
||||||
|
|
||||||
|
API‑Key erforderlich; kostenlose und bezahlte Pläne Übersicht/Pricing:
|
||||||
|
https://www.tracestrack.com/en/maps/ Nutzungsbedingungen: https://www.tracestrack.com/en/terms/
|
||||||
|
Empfehlung:
|
||||||
|
|
||||||
|
Produktion: Nimm Thunderforest oder Tracestrack (oder MapTiler: https://www.maptiler.com/pricing/)
|
||||||
|
oder hoste Tiles selbst. Attribution in der Karte anzeigen (OSM + jeweiliger Anbieter). Soll ich
|
||||||
|
diese Links samt klarer Hinweise kompakt in eure README.md einpflegen?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
[Zurück zur Übersicht](../README.md)
|
[Zurück zur Übersicht](../README.md)
|
||||||
|
|||||||
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "nodemap",
|
"name": "nodemap",
|
||||||
"version": "1.1.361",
|
"version": "1.1.382",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "nodemap",
|
"name": "nodemap",
|
||||||
"version": "1.1.361",
|
"version": "1.1.382",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.13.3",
|
"@emotion/react": "^11.13.3",
|
||||||
"@emotion/styled": "^11.13.0",
|
"@emotion/styled": "^11.13.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "nodemap",
|
"name": "nodemap",
|
||||||
"version": "1.1.361",
|
"version": "1.1.382",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.13.3",
|
"@emotion/react": "^11.13.3",
|
||||||
"@emotion/styled": "^11.13.0",
|
"@emotion/styled": "^11.13.0",
|
||||||
@@ -45,6 +45,11 @@
|
|||||||
"start": "cross-env NODE_ENV=production node server.js",
|
"start": "cross-env NODE_ENV=production node server.js",
|
||||||
"export": "next export",
|
"export": "next export",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
|
"test:e2e": "playwright test",
|
||||||
|
"test:e2e:ui": "playwright test --ui",
|
||||||
|
"test:e2e:slow": "cross-env PW_HEADED=1 PW_SLOWMO=1000 playwright test",
|
||||||
|
"test:e2e:slow:ui": "cross-env PW_HEADED=1 PW_SLOWMO=1000 playwright test --ui",
|
||||||
|
"test:e2e:report": "playwright show-report ./playwright/reports",
|
||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"bump-version": "node ./scripts/bumpVersion.js"
|
"bump-version": "node ./scripts/bumpVersion.js"
|
||||||
},
|
},
|
||||||
|
|||||||
35
playwright.config.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Playwright test configuration for the NodeMap project
|
||||||
|
// Starts the local Next.js custom server (server.js) and runs tests against http://localhost:3000
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const { defineConfig, devices } = require("@playwright/test");
|
||||||
|
|
||||||
|
module.exports = defineConfig({
|
||||||
|
testDir: "./playwright/tests",
|
||||||
|
timeout: 60_000,
|
||||||
|
expect: { timeout: 10_000 },
|
||||||
|
fullyParallel: true,
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
// Reporters: keep console-friendly list and generate an HTML report under playwright/reports
|
||||||
|
reporter: [["list"], ["html", { outputFolder: "playwright/reports", open: "never" }]],
|
||||||
|
// Store any runner outputs (attachments, logs) under playwright/test-results
|
||||||
|
outputDir: "playwright/test-results",
|
||||||
|
use: {
|
||||||
|
baseURL: "http://localhost:3000",
|
||||||
|
// Disable artifact generation locally to avoid creating files
|
||||||
|
trace: "off",
|
||||||
|
video: "off",
|
||||||
|
screenshot: "off",
|
||||||
|
headless: process.env.PW_HEADED ? false : true,
|
||||||
|
// Apply slow motion to all actions when PW_SLOWMO is set
|
||||||
|
launchOptions: {
|
||||||
|
slowMo: process.env.PW_SLOWMO ? Number(process.env.PW_SLOWMO) : 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: "chromium",
|
||||||
|
use: { ...devices["Desktop Chrome"] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// example.spec.js
|
|
||||||
const { test, expect } = require("@playwright/test");
|
|
||||||
|
|
||||||
test("simple test", async ({ page }) => {
|
|
||||||
await page.goto("https://playwright.dev");
|
|
||||||
await expect(page).toHaveTitle(/Playwright/);
|
|
||||||
});
|
|
||||||
119
playwright/tests/mapcomponent.spec.js
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
|
||||||
|
// Helper: robust selection for native <select> or custom ARIA comboboxes
|
||||||
|
async function selectStation(page, value) {
|
||||||
|
// Try to find by accessible name first
|
||||||
|
let combo = page.getByRole("combobox", { name: /Station wählen/i });
|
||||||
|
if (!(await combo.count())) {
|
||||||
|
// Fallback: find a container with the label text and locate a select inside
|
||||||
|
const container = page.locator("div").filter({ hasText: "Station wählen" }).last();
|
||||||
|
const selectInContainer = container.locator("select");
|
||||||
|
if (await selectInContainer.count()) {
|
||||||
|
combo = selectInContainer.first();
|
||||||
|
} else {
|
||||||
|
// Final fallback: first visible native select (overlay has only one)
|
||||||
|
combo = page.locator("select:visible").first();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await expect(combo).toBeVisible();
|
||||||
|
|
||||||
|
const isNative = await combo.evaluate(el => el.tagName === "SELECT");
|
||||||
|
if (isNative) {
|
||||||
|
await expect(combo).toBeEnabled();
|
||||||
|
await expect(combo.locator(`option[value="${value}"]`)).toBeAttached();
|
||||||
|
await combo.selectOption({ value });
|
||||||
|
} else {
|
||||||
|
await combo.click();
|
||||||
|
await page.getByRole("option", { name: new RegExp(value) }).click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test("MapComponent", async ({ page }) => {
|
||||||
|
// Set initial localStorage BEFORE navigation so the app reads them on load
|
||||||
|
await page.addInitScript(() => {
|
||||||
|
localStorage.setItem("editMode", "false");
|
||||||
|
localStorage.setItem("polylineVisible_m12_u484", "true");
|
||||||
|
localStorage.setItem("currentMapId", "12");
|
||||||
|
localStorage.setItem("currentUserId", "484");
|
||||||
|
localStorage.setItem("mapZoom", "13");
|
||||||
|
localStorage.setItem("kabelstreckenVisible", "false");
|
||||||
|
localStorage.setItem("showBaseMapPanel", "false");
|
||||||
|
localStorage.setItem(
|
||||||
|
"mapLayersVisibility_m12_u484",
|
||||||
|
JSON.stringify({
|
||||||
|
"system-1": true,
|
||||||
|
"system-2": false,
|
||||||
|
"system-3": false,
|
||||||
|
"system-5": false,
|
||||||
|
"system-6": false,
|
||||||
|
"system-7": false,
|
||||||
|
"system-8": false,
|
||||||
|
"system-9": false,
|
||||||
|
"system-10": false,
|
||||||
|
"system-11": false,
|
||||||
|
"system-13": false,
|
||||||
|
"system-30": false,
|
||||||
|
"system-100": false,
|
||||||
|
"system-110": false,
|
||||||
|
"system-111": false,
|
||||||
|
"system-200": false,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
localStorage.setItem("mapCenter", JSON.stringify([53.23938294961826, 8.21434020996094]));
|
||||||
|
localStorage.setItem("markerLink", "undefined");
|
||||||
|
localStorage.setItem("lastElementType", "marker");
|
||||||
|
localStorage.setItem("polylineVisible", "false");
|
||||||
|
localStorage.setItem("showAppInfoCard", "false");
|
||||||
|
localStorage.setItem("showCoordinateInput", "false");
|
||||||
|
localStorage.setItem("showLayersPanel", "false");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1) Navigate and wait for the map
|
||||||
|
await page.goto("http://localhost:3000/?m=12&u=484");
|
||||||
|
await page.locator("#map").waitFor({ state: "visible", timeout: 20_000 });
|
||||||
|
|
||||||
|
// 2) Optional: verify a key from localStorage at runtime
|
||||||
|
await expect(page.evaluate(() => localStorage.getItem("showLayersPanel"))).resolves.toBe("false");
|
||||||
|
|
||||||
|
// 3) Layer-Panel toggle: expect "einblenden" first (since showLayersPanel=false), then toggle
|
||||||
|
await expect(page.getByRole("button", { name: "Layer-Panel einblenden" })).toBeVisible();
|
||||||
|
await page.getByRole("button", { name: "Layer-Panel einblenden" }).click();
|
||||||
|
await expect(page.getByRole("button", { name: "Layer-Panel ausblenden" })).toBeVisible();
|
||||||
|
// 4) Collapse again to restore state
|
||||||
|
await page.getByRole("button", { name: "Layer-Panel ausblenden" }).click();
|
||||||
|
|
||||||
|
// 5) Info-Card toggle: start hidden -> show -> hide -> show again
|
||||||
|
await expect(page.getByRole("button", { name: "Info einblenden" })).toBeVisible();
|
||||||
|
await page.getByRole("button", { name: "Info einblenden" }).click();
|
||||||
|
await expect(page.getByRole("button", { name: "Info ausblenden" })).toBeVisible();
|
||||||
|
await page.getByRole("button", { name: "Info ausblenden" }).click();
|
||||||
|
await expect(page.getByRole("button", { name: "Info einblenden" })).toBeVisible();
|
||||||
|
await page.getByRole("button", { name: "Info einblenden" }).click();
|
||||||
|
await expect(page.locator("div").filter({ hasText: "TALAS.Map Version" }).nth(3)).toBeVisible();
|
||||||
|
|
||||||
|
// 6) Koordinatensuche toggle
|
||||||
|
await page.getByRole("button", { name: "Koordinatensuche einblenden" }).click();
|
||||||
|
await expect(page.locator("form")).toBeVisible();
|
||||||
|
await page.getByRole("button", { name: "Koordinatensuche ausblenden" }).click();
|
||||||
|
|
||||||
|
// 7) Marker setzen und Stationen wählen
|
||||||
|
await page.getByLabel("Marker").click();
|
||||||
|
await expect(page.getByText("Station wählenBitte wählen…")).toBeVisible();
|
||||||
|
await selectStation(page, "50977");
|
||||||
|
await page.getByLabel("Marker").click();
|
||||||
|
await selectStation(page, "50986");
|
||||||
|
await page.getByLabel("Marker").click();
|
||||||
|
await page.getByLabel("Marker").click();
|
||||||
|
await page.getByLabel("Marker").click();
|
||||||
|
await selectStation(page, "50977");
|
||||||
|
await page.getByRole("button", { name: "Karte auf Standardansicht" }).click();
|
||||||
|
//minusIcon
|
||||||
|
await page.getByTestId("zoom-out").click();
|
||||||
|
//wait 3 seconds
|
||||||
|
// plusIcon
|
||||||
|
await page.getByTestId("zoom-in").click(); //plus
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Powershell Befehl ->das führt langsam aus mit 1 Sekunde Pause zwischen den Aktionen
|
||||||
|
$env:PW_HEADED=1; $env:PW_SLOWMO=1000; npx playwright test
|
||||||
|
*/
|
||||||
@@ -5,22 +5,7 @@
|
|||||||
"zoomOutCenter: Zielkoordinaten für Herauszoomen (lat, lng)",
|
"zoomOutCenter: Zielkoordinaten für Herauszoomen (lat, lng)",
|
||||||
"minZoom/maxZoom: erlaubte Zoomstufen pro Quelle"
|
"minZoom/maxZoom: erlaubte Zoomstufen pro Quelle"
|
||||||
],
|
],
|
||||||
"tileSources": {
|
|
||||||
"local": {
|
|
||||||
"url": "http://localhost/talas5/TileMap/mapTiles/{z}/{x}/{y}.png",
|
|
||||||
"_comment": "Offline-Kartenquelle (lokal)",
|
|
||||||
"minZoom": 5,
|
|
||||||
"maxZoom": 15
|
|
||||||
},
|
|
||||||
"osm": {
|
|
||||||
"url": "/tiles/{z}/{x}/{y}.png",
|
|
||||||
"_comment": "OpenStreetMap Online-Kartenquelle über Server-Proxy (relativ)",
|
|
||||||
"minZoom": 0,
|
|
||||||
"maxZoom": 19
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"active": "osm",
|
|
||||||
"_comment_active": "Aktive Kartenquelle: 'local' oder 'osm'",
|
|
||||||
"center": [53.111111, 8.4625],
|
"center": [53.111111, 8.4625],
|
||||||
"_comment_center": "Startmittelpunkt der Karte (lat, lng)",
|
"_comment_center": "Startmittelpunkt der Karte (lat, lng)",
|
||||||
|
|
||||||
@@ -29,6 +14,9 @@
|
|||||||
|
|
||||||
"basePath": "/talas5",
|
"basePath": "/talas5",
|
||||||
"_comment_basePath": "Basis-URL für API und Routing",
|
"_comment_basePath": "Basis-URL für API und Routing",
|
||||||
|
"minZoom": 5,
|
||||||
|
"maxZoom": 20,
|
||||||
|
"_comment_zoom": "Globale Zoom-Grenzen (min/max). Kann durch tileSources überschrieben werden.",
|
||||||
"debugLog": false,
|
"debugLog": false,
|
||||||
"_comment_debugLog": "Debug-Logging für Client "
|
"_comment_debugLog": "Debug-Logging für Client "
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 349 B |
3
public/img/icons/material-symbols/alarm.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 396 B |
3
public/img/icons/material-symbols/edit-off.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L18 21l-3-3m-12.728-.364A9 9 0 015.636 5.636m0 0L3 3l3 3m9.364 9.364L18 21M5.636 5.636L3 3" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 351 B |
3
public/img/icons/material-symbols/edit.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 432 B |
3
public/img/icons/material-symbols/info.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 333 B |
4
public/img/icons/material-symbols/map-marker.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="rgb(0, 174, 239)" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25s-7.5-4.108-7.5-11.25a7.5 7.5 0 1115 0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 373 B |
3
public/img/icons/material-symbols/menu.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 231 B |
3
public/img/icons/material-symbols/search.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 261 B |
@@ -1,3 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<testsuites name="jest tests" tests="0" failures="0" errors="0" time="4.822">
|
|
||||||
</testsuites>
|
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: ["./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}", "./hooks/**/*.{js,ts,jsx,tsx}"],
|
content: [
|
||||||
|
"./pages/**/*.{js,ts,jsx,tsx}",
|
||||||
|
"./components/**/*.{js,ts,jsx,tsx}",
|
||||||
|
"./hooks/**/*.{js,ts,jsx,tsx}",
|
||||||
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
zIndex: {
|
zIndex: {
|
||||||
@@ -10,6 +14,9 @@ module.exports = {
|
|||||||
90: "90",
|
90: "90",
|
||||||
100: "100",
|
100: "100",
|
||||||
},
|
},
|
||||||
|
colors: {
|
||||||
|
"littwin-blue": "#00aeef",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"status": "passed",
|
|
||||||
"failedTests": []
|
|
||||||
}
|
|
||||||
@@ -67,7 +67,7 @@ export const initializeMap = (
|
|||||||
let mapCenter = [53.111111, 8.4625];
|
let mapCenter = [53.111111, 8.4625];
|
||||||
let mapZoom = 12;
|
let mapZoom = 12;
|
||||||
let minZoom = 5;
|
let minZoom = 5;
|
||||||
let maxZoom = 15;
|
let maxZoom = 20;
|
||||||
try {
|
try {
|
||||||
if (window && window.__leafletConfig) {
|
if (window && window.__leafletConfig) {
|
||||||
config = window.__leafletConfig;
|
config = window.__leafletConfig;
|
||||||
@@ -125,6 +125,7 @@ export const initializeMap = (
|
|||||||
zoom: mapZoom,
|
zoom: mapZoom,
|
||||||
minZoom: minZoom,
|
minZoom: minZoom,
|
||||||
maxZoom: maxZoom,
|
maxZoom: maxZoom,
|
||||||
|
// Disable default position; we'll add our own control at bottom-right
|
||||||
zoomControl: false,
|
zoomControl: false,
|
||||||
dragging: true,
|
dragging: true,
|
||||||
contextmenu: true,
|
contextmenu: true,
|
||||||
@@ -142,6 +143,8 @@ export const initializeMap = (
|
|||||||
|
|
||||||
initMap.dragging.enable();
|
initMap.dragging.enable();
|
||||||
|
|
||||||
|
// Do not add the default Leaflet zoom control; we'll render custom React controls in MapComponent
|
||||||
|
|
||||||
L.tileLayer(tileLayerUrl, {
|
L.tileLayer(tileLayerUrl, {
|
||||||
attribution: "© OpenStreetMap contributors",
|
attribution: "© OpenStreetMap contributors",
|
||||||
tileSize: 256,
|
tileSize: 256,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const zoomIn = (e, map) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let maxZoom = 19;
|
let maxZoom = 20;
|
||||||
try {
|
try {
|
||||||
if (window && window.__tileSourceMaxZoom !== undefined) {
|
if (window && window.__tileSourceMaxZoom !== undefined) {
|
||||||
maxZoom = window.__tileSourceMaxZoom;
|
maxZoom = window.__tileSourceMaxZoom;
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Eingang DE 01 kommend",
|
"Me": "Eingang DE 01 kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
@@ -15,7 +16,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Eingang DE 31 kommend",
|
"Me": "Eingang DE 31 kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
@@ -24,7 +26,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Eingang DE 17 kommend",
|
"Me": "Eingang DE 17 kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
@@ -33,7 +36,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Eingang DE 05 kommend",
|
"Me": "Eingang DE 05 kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50984,
|
"IdLD": 50984,
|
||||||
@@ -42,7 +46,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Eingang DE 20 kommend",
|
"Me": "Eingang DE 20 kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50975,
|
"IdLD": 50975,
|
||||||
@@ -51,7 +56,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Eingang DE 32 kommend",
|
"Me": "Eingang DE 32 kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50984,
|
"IdLD": 50984,
|
||||||
@@ -60,7 +66,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Eingang DE 01 kommend",
|
"Me": "Eingang DE 01 kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50977,
|
"IdLD": 50977,
|
||||||
@@ -69,7 +76,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Station offline",
|
"Me": "Station offline",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50977,
|
"IdLD": 50977,
|
||||||
@@ -78,7 +86,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Eingang DE 01 kommend",
|
"Me": "Eingang DE 01 kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50975,
|
"IdLD": 50975,
|
||||||
@@ -87,7 +96,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Station offline",
|
"Me": "Station offline",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50066,
|
"IdLD": 50066,
|
||||||
@@ -96,7 +106,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "CPL offline",
|
"Me": "CPL offline",
|
||||||
"Feld": 5,
|
"Feld": 5,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50011,
|
"IdLD": 50011,
|
||||||
@@ -105,7 +116,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "CPL offline",
|
"Me": "CPL offline",
|
||||||
"Feld": 16,
|
"Feld": 16,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50011,
|
"IdLD": 50011,
|
||||||
@@ -114,7 +126,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Wasserdruck aus",
|
"Me": "Wasserdruck aus",
|
||||||
"Feld": 16,
|
"Feld": 16,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50011,
|
"IdLD": 50011,
|
||||||
@@ -123,7 +136,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Ein",
|
"Me": "Ein",
|
||||||
"Feld": 16,
|
"Feld": 16,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50011,
|
"IdLD": 50011,
|
||||||
@@ -132,7 +146,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Digitaleingang 1 ON",
|
"Me": "Digitaleingang 1 ON",
|
||||||
"Feld": 16,
|
"Feld": 16,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50000,
|
"IdLD": 50000,
|
||||||
@@ -141,7 +156,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Ein",
|
"Me": "Ein",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
@@ -150,7 +166,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Station offline",
|
"Me": "Station offline",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
@@ -159,7 +176,8 @@
|
|||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Eingang DE 32 kommend",
|
"Me": "Eingang DE 32 kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50975,
|
"IdLD": 50975,
|
||||||
@@ -168,7 +186,8 @@
|
|||||||
"Co": "#FFFF00",
|
"Co": "#FFFF00",
|
||||||
"Me": "KÜG 07: Überspannung kommend",
|
"Me": "KÜG 07: Überspannung kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50975,
|
"IdLD": 50975,
|
||||||
@@ -177,7 +196,8 @@
|
|||||||
"Co": "#FFFF00",
|
"Co": "#FFFF00",
|
||||||
"Me": "KÜG 08: Überspannung gehend",
|
"Me": "KÜG 08: Überspannung gehend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50977,
|
"IdLD": 50977,
|
||||||
@@ -186,7 +206,8 @@
|
|||||||
"Co": "#FFFF00",
|
"Co": "#FFFF00",
|
||||||
"Me": "KÜG 08: Überspannung gehend",
|
"Me": "KÜG 08: Überspannung gehend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50984,
|
"IdLD": 50984,
|
||||||
@@ -195,7 +216,8 @@
|
|||||||
"Co": "#FFFF00",
|
"Co": "#FFFF00",
|
||||||
"Me": "KÜG 08: Überspannung gehend",
|
"Me": "KÜG 08: Überspannung gehend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
@@ -204,7 +226,8 @@
|
|||||||
"Co": "#FFFF00",
|
"Co": "#FFFF00",
|
||||||
"Me": "Eingang DE 02 kommend",
|
"Me": "Eingang DE 02 kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
@@ -213,7 +236,8 @@
|
|||||||
"Co": "#FFFF00",
|
"Co": "#FFFF00",
|
||||||
"Me": "KÜG 08: Überspannung gehend",
|
"Me": "KÜG 08: Überspannung gehend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
@@ -222,7 +246,8 @@
|
|||||||
"Co": "#FF9900",
|
"Co": "#FF9900",
|
||||||
"Me": "Eingang DE 03 Test Karte kommend",
|
"Me": "Eingang DE 03 Test Karte kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
@@ -231,7 +256,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 01: Aderbruch kommend",
|
"Me": "KÜG 01: Aderbruch kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
@@ -240,7 +266,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 02: Aderbruch kommend",
|
"Me": "KÜG 02: Aderbruch kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
@@ -249,7 +276,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 03: Aderbruch kommend",
|
"Me": "KÜG 03: Aderbruch kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50000,
|
"IdLD": 50000,
|
||||||
@@ -258,7 +286,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "über 8V kommend",
|
"Me": "über 8V kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50984,
|
"IdLD": 50984,
|
||||||
@@ -267,7 +296,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 05: Aderbruch kommend",
|
"Me": "KÜG 05: Aderbruch kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50984,
|
"IdLD": 50984,
|
||||||
@@ -276,7 +306,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 02: Isolationsminderung kommend",
|
"Me": "KÜG 02: Isolationsminderung kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50984,
|
"IdLD": 50984,
|
||||||
@@ -285,7 +316,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 06: Aderbruch kommend",
|
"Me": "KÜG 06: Aderbruch kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50977,
|
"IdLD": 50977,
|
||||||
@@ -294,7 +326,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 01: Isolationsminderung kommend",
|
"Me": "KÜG 01: Isolationsminderung kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50977,
|
"IdLD": 50977,
|
||||||
@@ -303,7 +336,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 06: Aderbruch kommend",
|
"Me": "KÜG 06: Aderbruch kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50977,
|
"IdLD": 50977,
|
||||||
@@ -312,7 +346,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 05: Aderbruch kommend",
|
"Me": "KÜG 05: Aderbruch kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50976,
|
"IdLD": 50976,
|
||||||
@@ -321,7 +356,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "CPL offline",
|
"Me": "CPL offline",
|
||||||
"Feld": 3,
|
"Feld": 3,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50976,
|
"IdLD": 50976,
|
||||||
@@ -330,7 +366,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 03: Isolationsminderung kommend",
|
"Me": "KÜG 03: Isolationsminderung kommend",
|
||||||
"Feld": 3,
|
"Feld": 3,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50975,
|
"IdLD": 50975,
|
||||||
@@ -339,7 +376,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 04: Isolationsminderung kommend",
|
"Me": "KÜG 04: Isolationsminderung kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50975,
|
"IdLD": 50975,
|
||||||
@@ -348,7 +386,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 02: Isolationsminderung kommend",
|
"Me": "KÜG 02: Isolationsminderung kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50975,
|
"IdLD": 50975,
|
||||||
@@ -357,7 +396,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 01: Isolationsminderung kommend",
|
"Me": "KÜG 01: Isolationsminderung kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50001,
|
"IdLD": 50001,
|
||||||
@@ -366,7 +406,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "Sammelstörung kommend",
|
"Me": "Sammelstörung kommend",
|
||||||
"Feld": 5,
|
"Feld": 5,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50975,
|
"IdLD": 50975,
|
||||||
@@ -375,7 +416,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 06: Aderbruch kommend",
|
"Me": "KÜG 06: Aderbruch kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50975,
|
"IdLD": 50975,
|
||||||
@@ -384,7 +426,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "KÜG 05: Aderbruch kommend",
|
"Me": "KÜG 05: Aderbruch kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50963,
|
"IdLD": 50963,
|
||||||
@@ -393,7 +436,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "CPL offline",
|
"Me": "CPL offline",
|
||||||
"Feld": 3,
|
"Feld": 3,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50063,
|
"IdLD": 50063,
|
||||||
@@ -402,7 +446,8 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "Digitaleingang 1 EIN",
|
"Me": "Digitaleingang 1 EIN",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50000,
|
"IdLD": 50000,
|
||||||
@@ -411,6 +456,7 @@
|
|||||||
"Co": "#FF0000",
|
"Co": "#FF0000",
|
||||||
"Me": "über 10V kommend",
|
"Me": "über 10V kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0,
|
||||||
|
"Alarm": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||