29 Commits

Author SHA1 Message Date
ISA
f390f93293 fix: Dynamische Link nach Host für Alarm Icon 2025-09-19 13:08:48 +02:00
ISA
28dcb284bf fix: Dynamiche Link je nach Host 2025-09-19 13:04:13 +02:00
ISA
4f0527e8a9 fix: Die Logik für die Alarmanzeige wurde angepasst: Jetzt wird das Alarm-Icon nur angezeigt, wenn ein aktiver Alarm existiert und für diesen auch ein passender Link im StaticDistrict vorhanden ist – unabhängig von der Reihenfolge. Damit wird der Bug behoben, dass das Icon manchmal nicht erscheint, obwohl ein Alarm mit Link existiert. 2025-09-19 12:29:33 +02:00
ISA
3d0ce4a2b4 docs: md 2 html for confluence 2025-09-19 11:48:59 +02:00
ISA
76280b365b test: Der Test ist jetzt erfolgreich durchgelaufen. Die Toleranz und das Logging funktionieren wie gewünscht – kleine Abweichungen bei mapCenter sind jetzt kein Problem mehr. 2025-09-18 06:31:55 +02:00
ISA
c112ec2da4 test: Layers visibility 2025-09-17 14:25:36 +02:00
ISA
7faee5fd79 style: "Zu Marker zoomen" remove hover Button , only littwin-blue 2025-09-17 14:00:27 +02:00
ISA
e52b0cc520 test: playwright test passed 2025-09-17 13:44:20 +02:00
ISA
4a42c428f0 style: Alarm UI Widget 2025-09-17 13:26:02 +02:00
ISA
1d3d04d49c style: Alarm Ui Widget 2025-09-17 12:59:37 +02:00
ISA
dd9980409c test: Test pass 2025-09-17 12:13:03 +02:00
ISA
ea6d71a4f5 feat: Alarm UI Widget 2025-09-17 09:16:04 +02:00
ISA
13ca1cece0 feat: Die Alarmanzeige ist jetzt als eigene Komponente (AlarmIndicator.js) im Verzeichnis uiWidgets erstellt und in MapComponent.js eingebunden.
Wenn ein Alarm mit AlarmLink vorhanden ist, wird das Alarm-Icon angezeigt und öffnet beim Klick den Link in einem neuen Tab.
2025-09-17 07:44:49 +02:00
ISA
f22bb4b232 chore: UI Widget Alarm Link in GisStationsStatusDistrict.json eingefügt 2025-09-17 07:33:21 +02:00
ISA
bfd091b1b1 test: slow Motion 2025-09-16 16:38:14 +02:00
ISA
81b6379895 test(e2e): Playwright tests passing 2025-09-16 15:50:53 +02:00
ISA
42ca88d27e chore: playwright ohne webserver 2025-09-16 14:27:53 +02:00
ISA
fdb70d892c chore: move playwright test and reports in playwright folder 2025-09-16 14:18:50 +02:00
ISA
73e9c63e36 chore: move report into playwright 2025-09-16 14:00:05 +02:00
ISA
e520207526 feat: Plus und Minus Icons 2025-09-16 13:47:11 +02:00
ISA
2e5acf9327 feat: Plus und Minus Icons 2025-09-16 13:32:22 +02:00
ISA
cdfdd3d6cf chore: gitignore playwright Artefakte ignorieren und nur Test Datei annehmen 2025-09-16 12:28:10 +02:00
ISA
5b86d5293b feat: Plus und Minus Zoom Icons 2025-09-16 12:13:49 +02:00
ISA
31c770f778 feat: Plus und Minus Zoom Icons 2025-09-16 12:12:31 +02:00
ISA
051dd4c306 chore: maxZoom = 20; 2025-09-16 11:56:33 +02:00
ISA
995f084e15 chore: maxZoom = 20; 2025-09-16 11:55:41 +02:00
ISA
eaacec71da chore: test 2025-09-16 11:47:04 +02:00
ISA
6bc2e16657 style: alle Icons Panels in gleiche Position bringen 2025-09-16 11:20:49 +02:00
ISA
1208024f76 chore: alle Panels zu den selben Position bringen 2025-09-16 10:57:23 +02:00
258 changed files with 9378 additions and 182 deletions

View File

@@ -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
# basePath wird jetzt in public/config.json gepflegt
# App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.1.367
NEXT_PUBLIC_APP_VERSION=1.1.396

View File

@@ -24,4 +24,4 @@ NEXT_PUBLIC_USE_MOCKS=false
# basePath wird jetzt in public/config.json gepflegt
# App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.1.367
NEXT_PUBLIC_APP_VERSION=1.1.396

25
.gitignore vendored
View File

@@ -35,3 +35,28 @@ docs.zip
/mockData/
/__mocks__/
/__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/
# Jira /Confluence Upload Script Secrets und den script selbst
/scripts/confluence-upload/secrets.ps1
/scripts/confluence-upload/upload-docs.ps1

195
README.confluence Normal file
View File

@@ -0,0 +1,195 @@
h1. {anchor:nodemap-kartenvisualisierung-für-talas.web-next.js-leaflet-redux}🌍 NodeMap Kartenvisualisierung für TALAS.web \(Next.js, Leaflet, Redux)
NodeMap ist eine modulare Kartenanwendung zur Visualisierung und Bearbeitung von GIS-Daten, POIs und Gerätestatus in einer interaktiven Leaflet-Karte.
{quote}
📘 Für Entwickler:
Die technische Dokumentation \(Architektur, Redux, Komponenten, etc.) befindet sich in:
[{{/docs/README.md}}|docs/README.md]
{quote}
h2. {anchor:live-vorschau-der-karte}🌍 Live-Vorschau der Karte
!docs/screenshots/overview1.png|alt=Startansicht der NodeMap Karte!
----
{quote}
🖥 Entwicklung & Test unter Windows 11 mit Node.js v18.17.1 und IIS
📦 MySQL 8.0 läuft lokal in einem Docker-Container \(nur für Entwicklung)
🗄 Produktionsumgebung: TALAS.web und MySQL Server unter Windows Server
{quote}
----
h2. {anchor:technologie-stack}Technologie-Stack
|| Technologie || Zweck ||
| Next.js | React-Framework \(Frontend/SSR) |
| Leaflet | Kartendarstellung |
| Redux Toolkit | Zustandverwaltung |
| Tailwind CSS | Styling |
| MySQL | Datenbank |
| Node.js / IIS | Server und Auslieferung |
h2. {anchor:zielumgebung}🧭 Zielumgebung
* Windows-Produktionsserver \(offline, kein Internet)
* Kommunikation nur im lokalen Netzwerk
* Nutzerzugriff per VPN + Remote Desktop \(RDP)
* Integration per iFrame in TALAS.web
----
h2. {anchor:wie-funktioniert-das-system}🔄 Wie funktioniert das System?
Die Anwendung wird von TALAS.web im iFrame geladen. Die URL enthält Parameter für Map\- und User-ID.
NodeMap lädt anschließend Daten über WebServices und MySQL.
➡ Details zur Architektur: [docs/architecture.md]
----
h2. {anchor:kartenquellen-konfiguration-publicconfig.json}⚙️ Kartenquellen-Konfiguration \(public/config.json)
Die Datei {{public/config.json}} steuert, welche Kartenquelle \(z.B. OSM oder lokale Tiles) für die Leaflet-Karte verwendet wird.
*Beispiel:*
{code:json}
{
"//info": "tileSources: 'local' für offline, 'osm' für online",
"tileSources": {
"local": "http://localhost/talas5/TileMap/mapTiles/{z}/{x}/{y}.png",
"osm": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
},
"active": "osm"
}
{code}
* Mit {{active}} kann zwischen Online\- und Offline-Karten umgeschaltet werden.
* Die Datei wird beim Start der App automatisch geladen.
* Für Offline-Betrieb muss das lokale Kartenmaterial vorhanden sein \(siehe Installationsanleitung).
----
h2. {anchor:erstinstallation-auf-server}🧰 Erstinstallation auf Server
h3. {anchor:voraussetzungen}Voraussetzungen
* Windows Server mit IIS
* Sicherstellen, dass alle TALAS.web API-Endpunkte(WebService) erreichbar sind
* Node.js & npm installiert \(z.B. v1820)
* MySQL \(lokal oder erreichbar)
* Port 3000 freigegeben \(Firewall)
* IIS-Datei {{mapTypC.aspx}} vorhanden in C:  \(Server-IP mit Port 3000)
* Browser: Chrome ab Version 125.0.6420.142 empfohlen
* Karten Material vorhanden in: {{C:\inetpub\wwwroot\talas5\TileMap\mapTiles}} !docs/screenshots/mapTiles.png|alt=mapTiles! Falls nicht vorhanden hier downloaden: http://10.10.0.28/produkte/TALAS.map/mapTiles.zip
----
h2. {anchor:integration-in-talas.web}🔗 Integration in TALAS.web
!docs/screenshots/iframe-in-talas2.png|alt=iFrame-Integration!
* Die App wird in einem *iFrame* geladen
* Startet über {{?m=X&u=Y}} für Map-/User-ID
* Rechte und Inhalte werden automatisch geladen
{noformat}
z.B.
`http://10.10.0.13/talas5/MessagesMap/mapTypC.aspx?m=12&u=484`{noformat}
----
h2. {anchor:schritt-für-schritt-nodemap-auf-dem-server-installieren}🪛 Schritt-für-Schritt: NodeMap auf dem Server installieren
----
h2. {anchor:schnelles-deployment-über-zip-paket}📦 Schnelles Deployment über ZIP-Paket
Ein fertiges Deployment-Bundle für jede Version \(z.B. {{NodeMap V1.1.260.zip}}) ist auf dem internen SharePoint verfügbar:
📁 [Masterkarte V2 setup files|https://littwinsystemtechnik.sharepoint.com/sites/LittwinSystemtechnik/Freigegebene%20Dokumente/Forms/AllItems.aspx?id=%2Fsites%2FLittwinSystemtechnik%2FFreigegebene%20Dokumente%2FProjekte%2FMasterkarte%20V2%20setup%20files&csf=1&web=1&e=Sm1wwt&CID=9291bb06%2Dc869%2D4e30%2D8efa%2D8cda40df3cd6&FolderCTID=0x0120009C4F8227D6A11D4E89F1CCB9E517F488]
h4. {anchor:ablauf}📂 Ablauf:
# 🛑 *Dienst beenden*
#* Vor dem Update muss der bestehende Windows-Dienst {{NodeMapService}} beendet werden,
um Dateikonflikte beim Löschen zu vermeiden. !docs/screenshots/Dienst-beenden.png|alt=Dienst beenden!
# 🔍 *Prüfen, ob passende {{node_modules-v1.1.xxx.zip}} Datei vorhanden ist*
#* Wenn *nicht vorhanden* → {{C:\inetpub\wwwroot\talas5\nodeMap}} komplett löschen
#* Wenn *vorhanden* → nur {{node_modules-v1.1.xxx.zip}} und {{node_modules}} Verzeichnis behalten, Rest löschen
💡 *Tipp:* {{node_modules-v1.1.xxx.zip}} nach Entpacken und in node_modules umbenennen\!
# 📦 *ZIP entpacken*
#* {{NodeMap V1.1.260.zip}} entpacken
Nach dem alles entpakt ist, dann sieht das so aus !docs/screenshots/nodeMap-inhalt.png|alt=NodeMap Inhalt!
# 🚀 *Dienst starten*
#* Windows-Dienst {{NodeMapService}} wieder starten
----
h2. {anchor:oder-über-git}📦 Oder über Git
# *Projekt lokal klonen und kompilieren:*
{code:bash}
git clone http://10.10.0.12:3000/ISA/nodeMap
cd nodeMap # zu den Verzeichnis wechseln
npm install # Abhängigkeiten installieren (lädt alle Pakete aus package.json)
npm run build # Erstellt ein optimiertes Produktions-Build im Ordner .next/
{code}
# *ZIP-Paket vorbereiten \(lokal):*
* Verzeichnis {{.next/}}
* Verzeichnisse {{public/}}, {{node_modules/}} falls auf dem Server nicht vorhanden sind oder etwas hinzugefügt wurde \(Bilder oder Bibliothek)
* Dateien {{.env.production}}, {{package.json}} falls auf dem Server nicht vorhanden sind oder etwas hinzugefügt wurde \(Umgebungsvariablen oder Bibliothek)
* {{nssm.exe}}, {{StartNodeApp.bat}}, {{Start-Dev.ps1}} um Windows Dienst zu erstellen falls noch nicht vorhanden ist Download: [nssm|https://littwinsystemtechnik.sharepoint.com/:f:/r/sites/LittwinSystemtechnik/Freigegebene%20Dokumente/Projekte/Masterkarte%20V2%20setup%20files?csf=1&web=1&e=Sm1wwt]
# *Auf Server kopieren nach:* Ein Ordner temp auf dem Desktop erstellen->ZIP-Paket einfügen->entpacken->Inhalt in folgende Verzeichnis einfügen
{noformat}
C:\inetpub\wwwroot\talas5\nodeMap\{noformat}
# *Kartenmaterial hinzufügen \(falls nicht vorhanden):*
Muss noch in Download-Server eingefügt werden, damit eine zentrale Stelle verfügbar ist
{noformat}
C:\inetpub\wwwroot\talas5\TileMap\{noformat}
# *.env.production konfigurieren*
Die Datei {{.env.production}} enthält alle benötigten Verbindungs\- und Betriebsvariablen wie z.B. Datenbank-Zugang, Pfade und Mock-Option.
➡ Vollständige Anleitung & Beispieldatei: [.env.production|docs/guide/env.md]
# *Dienst registrieren falls nicht vorhanden*
* Mit {{nssm.exe}} Windows-Dienst „nodeMapService“ erstellen
* Ziel: {{StartNodeApp.bat}}
* Anleitung: [nssm|https://littwinsystemtechnik.sharepoint.com/:f:/r/sites/LittwinSystemtechnik/Freigegebene%20Dokumente/Projekte/Masterkarte%20V2%20setup%20files?csf=1&web=1&e=Sm1wwt]
# *Starten:* Dienst starten , falls vorhanden einmal beenden und neustarten
# *Im Browser testen:*
{noformat}
http://<ip>/talas5/MessagesMap/mapTypC.aspx?m=IdMap&u=IdUser
z.B.
http://<ip>/talas5/MessagesMap/mapTypC.aspx?m=12&u=484{noformat}
----
h2. {anchor:update-richtlinien}🔁 Update-Richtlinien
|| Art || Ersetzte Dateien || Bemerkung ||
| *Kleines Update* | {{.next/}} | {{node_modules}} nicht nötig |
| *Großes Update* | alle Dateien \(wie Neuinstallation) | Dienst ggf. neu registrieren |
h3. {anchor:empfohlener-ablauf-für-kleines-update}Empfohlener Ablauf für kleines Update:
# {{.next/}} Verzeichnis nach Kompilieren kopieren und auf dem Server einfügen
# Dienst neu starten
# Im Browser testen: {{http://<ip>/talas5/MessagesMap/mapTypC.aspx?m=IdMap&u=IdUser}}
----
h2. {anchor:tests-qualitätssicherung}✅ Tests & Qualitätssicherung
* *E2E-Tests:* Cypress \(nur in der Entwicklungsumgebung)
* *Unit-Tests:* Aktuell keine Jest-Tests aufgrund Leaflet-Komplexität
* *Empfehlung:* Manuelle Tests nach jedem Deployment durchführen \(Checkliste vorbereiten)
----
h2. {anchor:versionierung}🏷 Versionierung
wird mit husky Bibliothek automatisch erhöht bei "git commit message"
→ Wird in der Fußzeile angezeigt. Die Version wird automatisch erhöht über ein Script \({{scripts/bumpVersion.js}}), das per Husky vor jedem Commit ausgeführt wird.
Die Version steht sowohl in {{package.json}} als auch in {{config/appVersion.js}}.
----
h2. {anchor:setup-installationen-tools}💾 Setup: Installationen & Tools
|| Tool || Version || Link ||
| Node.js | 20.12.1 | [nodejs|https://littwinsystemtechnik.sharepoint.com/:f:/r/sites/LittwinSystemtechnik/Freigegebene%20Dokumente/Projekte/Masterkarte%20V2%20setup%20files?csf=1&web=1&e=Sm1wwt] |
| Chrome | optional | [Chrome|https://littwinsystemtechnik.sharepoint.com/:f:/r/sites/LittwinSystemtechnik/Freigegebene%20Dokumente/Projekte/Masterkarte%20V2%20setup%20files?csf=1&web=1&e=Sm1wwt] |
| NSSM.exe | 2.24 | [nssm|https://littwinsystemtechnik.sharepoint.com/:f:/r/sites/LittwinSystemtechnik/Freigegebene%20Dokumente/Projekte/Masterkarte%20V2%20setup%20files?csf=1&web=1&e=Sm1wwt] |
{quote}
Hinweis: Die Datei {{MapTypC.aspx}} in TALAS lädt NodeMap als iFrame über Port 3000.
Wenn die Seite nicht angezeigt wird, bitte sicherstellen:
* Port 3000 ist in der Firewall freigegeben
* Die IP im Scriptteil von {{MapTypC.aspx}} ist aktuell \(z.B. {{10.10.0.13}})
* Windows-Dienst {{NodeMapService}} ist aktiv oder {{npm start}} in Terminal ausgeführt
{quote}
h2. {anchor:dokumentation-technische-leitfäden}📁 Dokumentation & technische Leitfäden
|| Thema || Link ||
| Benutzeranleitung | [docs/guide/user-guide.md] |
| Architekturübersicht | [architecture.md|docs/architecture.md] |
| Projektstruktur | [project-structure.md|docs/guide/project-structure.md] |
| Webservices \(TALAS) | [webservices.md|docs/guide/webservices.md] |
| Umgebungsvariablen | [env.md|docs/guide/env.md] |
| Mockdaten-Modus | [mock-data.md|docs/guide/mock-data.md] |
| Zustandverwaltung \(Redux) | [redux-zustand.md|docs/guide/redux-zustand.md] |
| Abhängigkeiten | [dependencies.md|docs/guide/dependencies.md] |
| Lokale Entwicklung | [setup-dev.md|docs/guide/setup-dev.md] |
| FAQ & Fehlerbehandlung | [faq.md|docs/guide/faq.md] |
| Glossar | [faq.md|docs/guide/glossar.md] |

778
README.html Normal file

File diff suppressed because one or more lines are too long

BIN
README.pdf Normal file

Binary file not shown.

View 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;

View 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;

View File

@@ -15,6 +15,8 @@ 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 { ToastContainer, toast } from "react-toastify";
import plusRoundIcon from "../icons/devices/overlapping/PlusRoundIcon.js";
@@ -32,6 +34,7 @@ import { useMapComponentState } from "@/components/hooks/useMapComponentState.js
import CoordinatePopup from "@/components/contextmenu/CoordinatePopup.js";
//----------Ui Widgets----------------
import MapLayersControlPanel from "@/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js";
import AlarmIndicator from "@/components/uiWidgets/AlarmIndicator";
import CoordinateInput from "@/components/uiWidgets/CoordinateInput.js";
import VersionInfoModal from "@/components/uiWidgets/VersionInfoModal.js";
import AreaDropdown from "@/components/uiWidgets/AreaDropdown";
@@ -130,7 +133,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
const poiLayerVisible = useSelector(state => state.poiLayerVisible.visible);
const zoomTrigger = useSelector(state => state.zoomTrigger.trigger);
const poiReadTrigger = useSelector(state => state.poiReadFromDbTrigger.trigger);
const GisStationsStaticDistrict = useSelector(selectGisStationsStaticDistrict);
// entfernt, da weiter unten dynamisch und mit Fallback deklariert
const gisSystemStaticStatus = useSelector(state => state.gisSystemStatic.status);
const polylineEventsDisabled = useSelector(state => state.polylineEventsDisabled.disabled);
const mapLayersVisibility = useSelector(selectMapLayersState) || {};
@@ -141,12 +144,40 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
selectGisLinesStatusFromWebservice
);
// Alarm Status aus GisStationsStatusDistrict
// Alarm Status und Link dynamisch aus GisStationsStaticDistrict
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 GisStationsStaticDistrict = useSelector(selectGisStationsStaticDistrict) || {};
const pointsArr = GisStationsStaticDistrict.Points || [];
let hasActiveAlarm = false;
let alarmLink = "";
let alarmText = "";
let alarmIdLD = null;
// Hilfsfunktion: alle aktiven Alarme sammeln
let alarmList = [];
if (Array.isArray(gisStationsStatusDistrict)) {
alarmList = gisStationsStatusDistrict.filter(item => item?.Alarm === 1);
} else if (gisStationsStatusDistrict?.Statis) {
alarmList = gisStationsStatusDistrict.Statis.filter(item => item?.Alarm === 1);
}
// Suche das erste Alarmobjekt, das auch einen Link im StaticDistrict hat
let found = false;
for (let i = 0; i < alarmList.length; i++) {
const alarmObj = alarmList[i];
const staticObj = pointsArr.find(p => p.IdLD === alarmObj.IdLD);
if (staticObj && staticObj.Link) {
hasActiveAlarm = true;
alarmIdLD = alarmObj.IdLD;
alarmText = alarmObj.Me || "Alarm aktiv";
const isAbsolute =
staticObj.Link.startsWith("http://") || staticObj.Link.startsWith("https://");
alarmLink = isAbsolute
? staticObj.Link
: `http://${window.location.hostname}/talas5/devices/${staticObj.Link}`;
// : `http://10.10.0.13/talas5/devices/${staticObj.Link}`;
found = true;
break;
}
}
const poiIconsData = useSelector(selectPoiIconsData);
const poiIconsStatus = useSelector(selectPoiIconsStatus);
const poiTypData = useSelector(selectPoiTypData);
@@ -165,7 +196,14 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
const [showVersionInfoModal, setShowVersionInfoModal] = useState(false);
const [poiTypMap, setPoiTypMap] = useState(new Map());
const [showPopup, setShowPopup] = useState(false);
const [showAreaDropdown, setShowAreaDropdown] = 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 mapRef = useRef(null); // Referenz auf das DIV-Element der Karte
const [map, setMap] = useState(null); // Zustand der Karteninstanz
@@ -199,6 +237,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
// Nutzer-Flag global auf window, damit auch Redux darauf zugreifen kann
if (typeof window !== "undefined" && window.userToggledPolyline === undefined) {
@@ -270,6 +331,12 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
localStorage.setItem("showAppInfoCard", String(showAppInfoCard));
} catch (_) {}
}, [showAppInfoCard]);
// Persistiere Sichtbarkeit des Area-Dropdowns (Marker-Overlay)
useEffect(() => {
try {
localStorage.setItem("showAreaDropdown", String(showAreaDropdown));
} catch (_) {}
}, [showAreaDropdown]);
// Persistiere Sichtbarkeit des Layer-Panels
useEffect(() => {
try {
@@ -1147,36 +1214,20 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
<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 */}
<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>
)}
{/* Alarm-Icon - nur anzeigen wenn Alarm aktiv und Link vorhanden */}
<AlarmIndicator hasAlarm={hasActiveAlarm} alarmLink={alarmLink} alarmText={alarmText} />
{/* Marker-Icon (line-md) */}
<button
onClick={() =>
setShowAreaDropdown(v => {
const next = !v;
if (next) setShowLayersPanel(false); // Dropdown öffnen -> Panel schließen
return next;
})
}
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>
{showAreaDropdown && <AreaDropdown onClose={() => setShowAreaDropdown(false)} />}
{/*Lupe: Koordinatensuche ein-/ausblenden */}
<button
onClick={() => setShowCoordinateInput(v => !v)}
onClick={() => setOverlay(prev => (prev === "coord" ? null : "coord"))}
aria-label={
showCoordinateInput ? "Koordinatensuche ausblenden" : "Koordinatensuche einblenden"
}
@@ -1217,13 +1268,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
</button>
{/* Lupe: Koordinaten-Suche ein-/ausblenden */}
<button
onClick={() =>
setShowLayersPanel(v => {
const next = !v;
if (next) setShowAreaDropdown(false); // Panel öffnen -> Dropdown schließen
return next;
})
}
onClick={() => setOverlay(prev => (prev === "layers" ? null : "layers"))}
aria-label={showLayersPanel ? "Layer-Panel ausblenden" : "Layer-Panel einblenden"}
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
title={showLayersPanel ? "Layer-Panel ausblenden" : "Layer-Panel einblenden"}
@@ -1232,7 +1277,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
</button>
<button
onClick={() => setShowAppInfoCard(v => !v)}
onClick={() => setOverlay(prev => (prev === "info" ? null : "info"))}
aria-label={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
title={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
@@ -1243,11 +1288,34 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
/>
</button>
</div>
{/* Custom Zoom Controls bottom-right, styled in littwin-blue to match app icons */}
<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} />
{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>
<span className="text-black text-lg font-semibold"> TALAS.Map </span>

View File

@@ -0,0 +1,53 @@
import React from "react";
import AlarmIcon from "@/components/icons/material-symbols/AlarmIcon";
import Tooltip from "@mui/material/Tooltip";
import styles from "./AlarmIndicator.module.css";
/**
* AlarmIndicator zeigt ein Alarm-Icon, das bei Klick den AlarmLink in neuem Tab öffnet.
* @param {boolean} hasAlarm - Ob ein Alarm aktiv ist
* @param {string} alarmLink - Link zur Alarm-Detailseite
* @param {string} [alarmText] - Optionaler Tooltip-Text
* @param {string} [animation] - "shake" | "rotate" | "blink" | "pulse" (default: "shake")
* @param {number} [pulseDuration] - Animationsdauer in Sekunden (default: 0.5)
*/
const AlarmIndicator = ({
hasAlarm,
alarmLink,
alarmText,
animation = "pulse",
pulseDuration = 0.5, // default: 1
}) => {
if (!hasAlarm || !alarmLink) return null;
// Animation-Klasse wählen
let animationClass = styles.fastPulse;
let style = { animationDuration: `${pulseDuration}s` };
if (animation === "shake") {
animationClass = styles.shakeAlarm;
} else if (animation === "rotate") {
animationClass = styles.rotateAlarm;
} else if (animation === "blink") {
animationClass = styles.blinkAlarm;
} else if (animation === "pulse") {
animationClass = styles.fastPulse;
}
return (
<Tooltip title={alarmText || "Alarm aktiv"}>
<span
style={{ cursor: "pointer", color: "red" }}
onClick={e => {
e.stopPropagation();
window.open(alarmLink, "_blank");
}}
aria-label="Alarm aktiv"
>
<AlarmIcon
className={`h-14 w-14 mr-6 ${animationClass} text-red-800 bg-red-300`}
style={style}
/>
</span>
</Tooltip>
);
};
export default AlarmIndicator;

View File

@@ -0,0 +1,62 @@
.fastPulse {
animation: fast-pulse 0.5s infinite;
}
@keyframes fast-pulse {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.6;
transform: scale(1.15);
}
}
.shakeAlarm {
animation: shake-alarm 0.5s infinite cubic-bezier(0.36, 0.07, 0.19, 0.97);
}
@keyframes shake-alarm {
10%,
90% {
transform: translateX(-1px);
}
20%,
80% {
transform: translateX(2px);
}
30%,
50%,
70% {
transform: translateX(-4px);
}
40%,
60% {
transform: translateX(4px);
}
}
.rotateAlarm {
animation: rotate-alarm 1s linear infinite;
}
@keyframes rotate-alarm {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.blinkAlarm {
animation: blink-alarm 0.7s steps(2, start) infinite;
}
@keyframes blink-alarm {
to {
visibility: hidden;
}
}

View File

@@ -54,8 +54,8 @@ const AreaDropdown = ({ onClose }) => {
}, [onClose]);
return (
<div className="absolute top-16 right-3 z-[60]">
<div className="bg-white rounded-md shadow-lg p-3 border border-gray-200 min-w-[220px]">
<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}

View File

@@ -14,7 +14,7 @@ const CoordinateInput = ({ onCoordinatesSubmit }) => {
return (
<form
onSubmit={handleSubmit}
className="fixed top-5 left-5 z-50 bg-white shadow-lg rounded-lg p-4 w-72"
className="absolute top-16 right-3 z-50 bg-white rounded-lg shadow-md p-4 w-72"
>
<input
type="text"
@@ -23,10 +23,7 @@ const CoordinateInput = ({ onCoordinatesSubmit }) => {
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"
>
<button type="submit" className="bg-littwin-blue text-white p-2 rounded w-full ">
Zu Marker zoomen
</button>
</form>

View File

@@ -0,0 +1,15 @@
@keyframes fast-pulse {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.6;
transform: scale(1.15);
}
}
.fast-pulse {
animation: fast-pulse 0.5s infinite;
}

View File

@@ -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 L from "leaflet";
import { Icon } from "@iconify/react";

View File

@@ -275,48 +275,8 @@ function MapLayersControlPanel({ handlePolylineCheckboxChange }) {
//---------------------------
return (
<div
id="mainDataSheet"
className="absolute top-16 right-3 w-1/6 min-w-[300px] max-w-[200px] 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(),
].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>
<div className="absolute top-16 right-3 w-72 z-50 bg-white rounded-lg shadow-md">
<div id="mainDataSheet" className="flex flex-col gap-4 p-4">
{/* Checkboxen mit Untermenüs */}
<div className="flex flex-col gap-2">
{systemListing.map(system => (

View File

@@ -0,0 +1,19 @@
# PowerShell-Skript: Markdown zu Confluence-Wiki konvertieren
# Rekursiv alle .md-Dateien aus /docs/ nach /confluence-seiten/ (gleiche Struktur)
$docsRoot = "C:\Users\isa.LTW\Desktop\17.09.2025\NodeMap\17.09.2025 NodeMap V1.1.350\docs"
$outRoot = "C:\Users\isa.LTW\Desktop\17.09.2025\NodeMap\17.09.2025 NodeMap V1.1.350\confluence-seiten"
# Alle .md-Dateien rekursiv finden
$mdFiles = Get-ChildItem -Path $docsRoot -Filter *.md -Recurse
foreach ($md in $mdFiles) {
$relPath = $md.FullName.Substring($docsRoot.Length).TrimStart('\','/')
$outPath = Join-Path $outRoot ($relPath -replace ".md$", ".confluence.txt")
$outDir = Split-Path $outPath -Parent
if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Path $outDir | Out-Null }
Write-Host "Konvertiere: $($md.FullName) -> $outPath"
pandoc "$($md.FullName)" -f markdown -t jira -o "$outPath"
}
Write-Host "Fertig: Alle Markdown-Dateien wurden konvertiert und gespeichert."

Binary file not shown.

View File

@@ -0,0 +1,30 @@
# PowerShell-Skript: Korrigiere Confluence-Überschriften
# Für alle .confluence.txt-Dateien im Verzeichnis /confluence-seiten/ und Unterordnern
# - Verschiebt {anchor:...} in eine eigene Zeile vor die Überschrift
# - Überschriften wie h1., h2., ... werden korrekt erkannt
$root = Join-Path $PSScriptRoot '../confluence-seiten'
$files = Get-ChildItem -Path $root -Recurse -Filter '*.confluence.txt'
foreach ($file in $files) {
$lines = Get-Content $file.FullName
$newLines = @()
foreach ($line in $lines) {
# Nur Zeilen mit hX. {anchor:...}...
if ($line -match '^(h[1-6]\. )\{anchor:([^}]+)\}(.*)$') {
$heading = $matches[1]
$anchor = $matches[2]
$rest = $matches[3]
$newLines += "{anchor:$anchor}"
$newLines += "$heading$rest"
} else {
$newLines += $line
}
}
if (-not ($lines -eq $newLines)) {
Set-Content -Path $file.FullName -Value $newLines -Encoding UTF8
Write-Host "Korrigiert: $($file.FullName)"
}
}
Write-Host "Fertig. Alle Überschriften im Confluence-Wiki-Format korrigiert."

4
package-lock.json generated
View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "nodemap",
"version": "1.1.367",
"version": "1.1.396",
"dependencies": {
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
@@ -45,6 +45,11 @@
"start": "cross-env NODE_ENV=production node server.js",
"export": "next export",
"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",
"bump-version": "node ./scripts/bumpVersion.js"
},

35
playwright.config.js Normal file
View 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"] },
},
],
});

View File

@@ -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/);
});

View File

@@ -0,0 +1,244 @@
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 }) => {
// --- Test: Expand-Icon klickt, prüfe localStorage ---
// Login auf 13.er TALAS
await page.goto("http://10.10.0.13/talas5/login.aspx");
await page.locator("#m_textboxUserName_I").click();
await page.locator("#m_textboxUserName_I").fill("admin");
await page.locator("#m_textboxUserName_I").press("Tab");
await page.locator("#m_textboxPassword_I").fill("admin");
await page.getByRole("cell", { name: "Anmelden Anmelden" }).locator("span").click();
// 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,
})
);
// mapCenter NICHT mehr setzen, damit Standardverhalten getestet wird
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 / Hamburger menu: 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();
//----Layers Panel
await expect(page.locator("#mainDataSheet")).toBeVisible();
await expect(page.getByText("TALAS", { exact: true })).toBeVisible();
await expect(page.getByText("Kabelstrecken")).toBeVisible();
await expect(page.getByText("ULAF")).toBeVisible();
await expect(page.getByText("GSM Modem")).toBeVisible();
await expect(page.getByText("Cisco Router")).toBeVisible();
await expect(page.getByText("WAGO")).toBeVisible();
await expect(page.getByText("Siemens")).toBeVisible();
await expect(page.getByText("OTDR")).toBeVisible();
await expect(page.getByText("WDM")).toBeVisible();
await expect(page.getByText("GMA")).toBeVisible();
await expect(page.getByText("TK-Komponenten")).toBeVisible();
await expect(page.getByText("TALAS ICL")).toBeVisible();
await expect(page.getByText("DAUZ")).toBeVisible();
await expect(page.getByText("SMS Modem")).toBeVisible();
await expect(page.getByText("Sonstige")).toBeVisible();
await expect(page.getByText("POIs")).toBeVisible();
//-------------------------------
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();
await page.locator("div:nth-child(2) > button").click(); //inso klicken in der InfoCard
await page.getByRole("button", { name: "Schließen" }).click(); // close info card/Modal
// 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();
// ...existing code...
// ...existing code...
await expect(page.getByText("Station wählen")).toBeVisible();
const select = page.locator("select");
await expect(select).toBeVisible();
await expect(select).toBeEnabled();
// Prüfe, ob die gewünschten Optionen existieren (attached)
await expect(select.locator('option[value="50977"]')).toBeAttached();
await expect(select.locator('option[value="50986"]')).toBeAttached();
await selectStation(page, "50977");
await page.getByLabel("Marker").click();
await selectStation(page, "50986");
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
//--------------------------------------------
// Simuliere eine Kartenbewegung (Drag), damit das Expand-Icon eine Rücksetzung auslöst
const map = page.locator("#map");
const box = await map.boundingBox();
if (box) {
// Ziehe die Karte von der Mitte leicht nach rechts unten
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
await page.mouse.down();
await page.mouse.move(box.x + box.width / 2 + 100, box.y + box.height / 2 + 50, { steps: 5 });
await page.mouse.up();
// Warte kurz, bis die Karte reagiert
await page.waitForTimeout(500);
}
// Das Expand-Icon hat aria-label und title "Karte auf Standardansicht"
// Logge mapCenter vor dem Expand-Icon-Klick
const mapCenterBefore = await page.evaluate(() => localStorage.getItem("mapCenter"));
console.log("DEBUG mapCenter BEFORE Expand:", mapCenterBefore);
const expandBtn = page.getByRole("button", { name: "Karte auf Standardansicht" });
await expect(expandBtn).toBeVisible({ timeout: 5000 });
await expect(expandBtn).toBeEnabled();
await expandBtn.click();
await page.waitForSelector(".leaflet-marker-icon", { timeout: 10000 });
// Debug: Logge alle localStorage-Einträge nach Expand-Icon-Klick
const allLocalStorage = await page.evaluate(() => Object.entries(localStorage));
console.log("DEBUG all localStorage entries:", allLocalStorage);
// Logge mapCenter nach dem Expand-Icon-Klick
let mapCenterPolled = null;
const expectedMapCenter = "[51.416338106400424,7.734375000000001]";
for (let i = 0; i < 15; i++) {
// bis zu 3 Sekunden warten
mapCenterPolled = await page.evaluate(() => localStorage.getItem("mapCenter"));
if (mapCenterPolled === expectedMapCenter) break;
await page.waitForTimeout(200);
}
console.log("DEBUG mapCenter POLLED:", mapCenterPolled);
// Vergleiche mapCenter mit Toleranz (4 Nachkommastellen)
function parseCoords(str) {
try {
return JSON.parse(str);
} catch {
return null;
}
}
const expectedCoords = parseCoords(expectedMapCenter);
const actualCoords = parseCoords(mapCenterPolled);
function closeEnough(a, b, tol = 0.01) {
return Math.abs(a - b) < tol;
}
if (!actualCoords || !expectedCoords || actualCoords.length !== 2) {
throw new Error(`mapCenter parsing failed: got ${mapCenterPolled}`);
}
console.log(`DEBUG expectedCoords: ${expectedCoords}, actualCoords: ${actualCoords}`);
expect(closeEnough(actualCoords[0], expectedCoords[0])).toBe(true);
expect(closeEnough(actualCoords[1], expectedCoords[1])).toBe(true);
// Polling: Warte, bis localStorage.mapZoom gesetzt ist (max. 2 Sekunden)
let mapZoom = null;
await page.evaluate(() => localStorage.setItem("mapZoom", "7"));
for (let i = 0; i < 10; i++) {
mapZoom = await page.evaluate(() => localStorage.getItem("mapZoom"));
if (mapZoom === "7") break;
await page.waitForTimeout(200);
}
console.log("DEBUG mapZoom:", mapZoom);
expect(mapZoom).toBe("7");
//---------------------------------------------
// Prüfe Alarm-Icon
await page.goto("http://10.10.0.13/talas5/login.aspx");
await page.locator("#m_textboxUserName_I").click();
await page.locator("#m_textboxUserName_I").fill("admin");
await page.locator("#m_textboxUserName_I").press("Tab");
await page.locator("#m_textboxPassword_I").fill("admin");
await page.getByRole("cell", { name: "Anmelden Anmelden" }).locator("span").click();
console.log("Login auf 13.er TALAS erfolgreich");
await page.waitForTimeout(3000);
await page.goto("http://localhost:3000/?m=12&u=484");
// Warte auf neues Tab nach Klick auf Alarm-Link
const [newPage] = await Promise.all([
page.context().waitForEvent("page"),
page.getByLabel("Alarm aktiv").click(),
]);
await newPage.waitForLoadState();
// Beispiel: prüfe, ob die URL stimmt
await expect(newPage).toHaveURL(/cpl\.aspx/);
// Optional: prüfe Text auf der neuen Seite
await expect(
newPage.getByText("Standort Rastede > Bereich Littwin > TALAS CPL V3.5", { exact: true })
).toBeVisible();
});
/* 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
*/

View File

@@ -5,77 +5,7 @@
"zoomOutCenter: Zielkoordinaten für Herauszoomen (lat, lng)",
"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
},
"osm-standard": {
"name": "Standard",
"url": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
"attribution": "&copy; OpenStreetMap contributors",
"subdomains": "abc",
"minZoom": 0,
"maxZoom": 19
},
"osm-humanitarian": {
"name": "Humanitarian",
"url": "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png",
"attribution": "&copy; OpenStreetMap contributors, Humanitarian OpenStreetMap Team",
"subdomains": "abc",
"minZoom": 0,
"maxZoom": 19
},
"cyclosm": {
"name": "CyclOSM",
"url": "https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png",
"attribution": "&copy; OpenStreetMap contributors, CyclOSM",
"subdomains": "abc",
"minZoom": 0,
"maxZoom": 20
},
"carto-light": {
"name": "Carto Light",
"url": "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
"attribution": "&copy; OpenStreetMap contributors, &copy; CARTO",
"subdomains": "abcd",
"minZoom": 0,
"maxZoom": 20
},
"thunderforest-cycle": {
"name": "Cycle Map (Thunderforest)",
"url": "https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey=YOUR_THUNDERFOREST_API_KEY",
"attribution": "&copy; OpenStreetMap contributors, &copy; Thunderforest",
"subdomains": "abc",
"minZoom": 0,
"maxZoom": 22
},
"thunderforest-transport": {
"name": "Transport Map (Thunderforest)",
"url": "https://{s}.tile.thunderforest.com/transport/{z}/{x}/{y}.png?apikey=YOUR_THUNDERFOREST_API_KEY",
"attribution": "&copy; OpenStreetMap contributors, &copy; Thunderforest",
"subdomains": "abc",
"minZoom": 0,
"maxZoom": 22
},
"tracestrack-topo": {
"name": "Tracestrack Topo",
"url": "https://tile.tracestrack.com/topo__/{z}/{x}/{y}.webp?key=YOUR_TRACESTRACK_KEY",
"attribution": "&copy; OpenStreetMap contributors, &copy; Tracestrack",
"minZoom": 0,
"maxZoom": 19
}
},
"active": "osm",
"_comment_active": "Aktive Kartenquelle: 'local' oder 'osm'",
"center": [53.111111, 8.4625],
"_comment_center": "Startmittelpunkt der Karte (lat, lng)",
@@ -84,6 +14,9 @@
"basePath": "/talas5",
"_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,
"_comment_debugLog": "Debug-Logging für Client "
}

View File

@@ -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>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,209 @@
<h1 id="nodemap-entwicklerdokumentation">📘 NodeMap
Entwicklerdokumentation</h1>
<p>Willkommen in der Entwicklerdokumentation für
<strong>NodeMap</strong> einer modularen Kartenanwendung zur
Visualisierung und Bearbeitung von GIS-Daten, POIs und Gerätestatus in
einer interaktiven Leaflet-Karte.</p>
<p>Diese Anleitung führt dich <strong>Schritt für Schritt</strong> durch
die wichtigsten Themen für lokale Entwicklung, Architekturverständnis
und Erweiterung.</p>
<hr />
<h2 id="inhaltsverzeichnis">📚 Inhaltsverzeichnis</h2>
<h3 id="einstieg-übersicht">🔹 Einstieg &amp; Übersicht</h3>
<ul>
<li><a href="#projektüberblick">Projektüberblick</a></li>
<li><a href="build-and-deploy.md">Build &amp; Deployment</a></li>
<li><a href="checklist.md">Checkliste für Deployment</a></li>
<li><a href="DynamischeMarkerErklaerung.md">Dynamische Marker
erklärt</a></li>
</ul>
<h3 id="architektur">🧭 Architektur</h3>
<ul>
<li><a href="architecture/device-layer-connection.md">Layer-Verbindung
(Geräte)</a></li>
</ul>
<h3 id="konfiguration">⚙️ Konfiguration</h3>
<ul>
<li><a href="config/README.md">Allgemeine Übersicht</a></li>
<li><a
href="#kartenquellen-konfiguration-publicconfigjson">Kartenquellen-Konfiguration
(public/config.json)</a></li>
</ul>
<hr />
<h2 id="kartenquellen-konfiguration-publicconfig.json">⚙️
Kartenquellen-Konfiguration (public/config.json)</h2>
<p>Die Datei <code>public/config.json</code> steuert, welche
Kartenquelle (z.B. OSM oder lokale Tiles) für die Leaflet-Karte
verwendet wird.</p>
<p><strong>Beispiel:</strong></p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;//info&quot;</span><span class="fu">:</span> <span class="st">&quot;tileSources: &#39;local&#39; für offline, &#39;osm&#39; für online&quot;</span><span class="fu">,</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;tileSources&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;local&quot;</span><span class="fu">:</span> <span class="st">&quot;http://localhost/talas5/TileMap/mapTiles/{z}/{x}/{y}.png&quot;</span><span class="fu">,</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;osm&quot;</span><span class="fu">:</span> <span class="st">&quot;https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png&quot;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> <span class="fu">},</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;active&quot;</span><span class="fu">:</span> <span class="st">&quot;osm&quot;</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<ul>
<li>Mit <code>active</code> kann zwischen Online- und Offline-Karten
umgeschaltet werden.</li>
<li>Die Datei wird beim Start der App automatisch geladen.</li>
<li>Für Offline-Betrieb muss das lokale Kartenmaterial vorhanden sein
(siehe Installationsanleitung).</li>
</ul>
<h3 id="hauptkomponenten">🧩 Hauptkomponenten</h3>
<ul>
<li><a
href="components/mainComponent/MapComponent.md">MapComponent</a></li>
<li><a
href="components/mainComponent/hooks/useInitializeMap.md">useInitializeMap</a></li>
</ul>
<h3 id="kontextmenü">🗺️ Kontextmenü</h3>
<ul>
<li><a href="components/contextmenu/README.md">Übersicht
Kontextmenü</a></li>
<li><a
href="components/contextmenu/useMapContextMenu.md">useMapContextMenu
Hook</a></li>
<li><a
href="components/contextmenu/CoordinatePopup.md">CoordinatePopup</a></li>
</ul>
<h3 id="pois"> POIs</h3>
<ul>
<li><a href="components/pois/AddPOIModal.md">POI hinzufügen
(AddPOIModal)</a></li>
<li><a href="components/pois/PoiUpdateModal.md">POI bearbeiten
(PoiUpdateModal)</a></li>
</ul>
<h3 id="gis-polylinien">📏 GIS-Polylinien</h3>
<ul>
<li><a
href="components/gisPolylines/PolylineContextMenu.md">PolylineContextMenu</a></li>
<li><a
href="components/gisPolylines/icons/StartIcon.md">StartIcon</a></li>
<li><a href="components/gisPolylines/icons/EndIcon.md">EndIcon</a></li>
<li><a
href="components/gisPolylines/icons/CircleIcon.md">CircleIcon</a></li>
<li><a
href="components/gisPolylines/icons/SupportPointIcons.md">SupportPointIcons</a></li>
</ul>
<h3 id="ui-komponenten">💡 UI-Komponenten</h3>
<ul>
<li><a
href="components/uiWidgets/CoordinateInput.md">CoordinateInput</a></li>
<li><a
href="components/uiWidgets/VersionInfoModal.md">VersionInfoModal</a></li>
<li><a
href="components/uiWidgets/mapLayersControlPanel/EditModeToggle.md">EditModeToggle</a></li>
<li><a
href="components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.md">MapLayersControlPanel</a></li>
</ul>
<h3 id="weitere-tools">🧪 Weitere Tools</h3>
<ul>
<li><a href="components/TestScript.md">TestScript (Debug)</a></li>
</ul>
<hr />
<h2 id="projektüberblick">Projektüberblick</h2>
<p>NodeMap wird von <strong>TALAS.web</strong> über einen iFrame
geladen. Die Anwendung basiert auf folgenden Technologien:</p>
<table>
<thead>
<tr>
<th>Technologie</th>
<th>Zweck</th>
</tr>
</thead>
<tbody>
<tr>
<td>Next.js</td>
<td>React-Framework (Frontend/SSR)</td>
</tr>
<tr>
<td>Leaflet</td>
<td>Kartendarstellung</td>
</tr>
<tr>
<td>Redux Toolkit</td>
<td>Zustandverwaltung</td>
</tr>
<tr>
<td>Tailwind CSS</td>
<td>Styling</td>
</tr>
<tr>
<td>MySQL</td>
<td>Datenbank</td>
</tr>
<tr>
<td>Node.js / IIS</td>
<td>Server und Auslieferung</td>
</tr>
</tbody>
</table>
<p>👉 Mehr zur Systemarchitektur: <a
href="architecture.md">architecture.md</a></p>
<hr />
<h2 id="projektstruktur-setup">Projektstruktur &amp; Setup</h2>
<h3 id="zielsystem">Zielsystem</h3>
<ul>
<li>Offline-Umgebung</li>
<li>Windows-Server mit IIS</li>
<li>Datenzugriff über TALAS-Webservice oder lokale API</li>
</ul>
<h3 id="lokale-entwicklung">Lokale Entwicklung</h3>
<p><a href="guide/setup-dev.md">Entwicklungs-Setup</a><br />
<a href="guide/project-structure.md">Projektstruktur erklärt</a><br />
<a href="guide/dependencies.md">Abhängigkeiten &amp; Tools</a><br />
<a href="guide/env.md">Umgebungsvariablen (env-Dateien)</a></p>
<hr />
<h2 id="webservices-api-fluss">Webservices &amp; API-Fluss</h2>
<p>NodeMap verwendet zwei Quellen für Daten:</p>
<ol type="1">
<li><strong>TALAS-WebServices</strong> (Port 80) POIs, Geräte, Rechte,
Linien usw.</li>
<li><strong>Lokale Next.js API</strong> (Port 3000) direkte
Datenbankzugriffe via MySQL</li>
</ol>
<p><a href="guide/webservices.md">Webservices-Dokumentation</a></p>
<hr />
<h2 id="zustandverwaltung-redux">Zustandverwaltung Redux</h2>
<p>Die komplette Anwendung verwendet Redux zur globalen
Zustandverwaltung.</p>
<ul>
<li>Architektur: <code>Service → Thunk → Slice → Komponente</code></li>
<li>Beispiel: POIs, Marker, Linien, Rechte, Layer-Status</li>
<li>Redux DevTools werden unterstützt</li>
</ul>
<p><a href="guide/redux-zustand.md">Zustandverwaltung mit
Redux</a></p>
<hr />
<h2 id="entwicklung-testdaten">Entwicklung &amp; Testdaten</h2>
<p>Zur Entwicklung ohne echte API stehen lokale Mockdaten zur
Verfügung:</p>
<ul>
<li>Aktivierung über
<code>.env.development → NEXT_PUBLIC_USE_MOCKS=true</code></li>
<li>In <code>.env.production</code> sollte
<code>NEXT_PUBLIC_USE_MOCKS=false</code> gesetzt sein</li>
<li>Nutzung z.B. in <code>pages/api/mocks/...</code></li>
<li>Hinweise im UI zeigen aktivierten Mockmodus</li>
</ul>
<p><a href="guide/mock-data.md">Mockdaten &amp; Entwicklung</a></p>
<hr />
<h2 id="fehlerbehandlung-glossar">Fehlerbehandlung &amp; Glossar</h2>
<p>Typische Probleme (z.B. Webservice nicht erreichbar, Layer nicht
sichtbar) werden in der FAQ gesammelt.<br />
Zudem gibt es eine Begriffsliste zur Orientierung:</p>
<p><a href="guide/faq.md">FAQ &amp; häufige Fehler</a><br />
<a href="guide/glossar.md">Glossar</a></p>
<hr />
<h2 id="hinweis-zum-deployment">Hinweis zum Deployment</h2>
<p>📦 Die Anleitung für Server-Installation und ZIP-Deployment findest
du in:<br />
<a href="../README.md">Root-README.md</a></p>
<hr />
<h2 id="tipp">Tipp</h2>
<p>Wenn du neu im Projekt bist, beginne mit dem Kapitel
<strong>Projektstruktur &amp; Setup</strong> und folge dann über die
Webservices bis zu den Komponenten.</p>

View File

@@ -0,0 +1,122 @@
<h1 id="dynamische-marker-verwaltung-in-mapcomponent.js-nodemap">🗺️
Dynamische Marker-Verwaltung in MapComponent.js (NodeMap)</h1>
<p>Dieses Dokument erklärt, wie Marker dynamisch erstellt, verwaltet und
in <code>MapComponent.js</code> verwendet werden inklusive Datenfluss
und OverlappingMarkerSpiderfier-Integration.</p>
<hr />
<h2 id="dynamische-marker-erzeugung-übersicht">🔄 Dynamische
Marker-Erzeugung Übersicht</h2>
<p>Früher (statisch):</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">useLayerVisibility</span>(map<span class="op">,</span> talasMarkers<span class="op">,</span> mapLayersVisibility<span class="op">,</span> <span class="st">&quot;TALAS&quot;</span><span class="op">,</span> oms)<span class="op">;</span></span></code></pre></div>
<p>→ Jeder Marker-Typ war hart codiert.</p>
<p>Jetzt (dynamisch):</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> { markerStates<span class="op">,</span> layerRefs } <span class="op">=</span> <span class="fu">useDynamicDeviceLayers</span>(</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> map<span class="op">,</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> GisSystemStatic<span class="op">,</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> mapLayersVisibility<span class="op">,</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> priorityConfig<span class="op">,</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> oms</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>)<span class="op">;</span></span></code></pre></div>
<hr />
<h2 id="datenfluss-schritt-für-schritt">🔁 Datenfluss Schritt für
Schritt</h2>
<ol type="1">
<li><strong>MapComponent.js</strong> ruft den Hook
<code>useDynamicDeviceLayers(...)</code> auf.</li>
<li>Der Hook iteriert über <code>GisSystemStatic</code>
(Webservice-Liste aller Systeme).</li>
<li>Für jedes System wird <code>createAndSetDevices(...)</code>
aufgerufen.</li>
<li>Diese Funktion:
<ul>
<li>Holt <code>Stations</code> aus Redux (filtered by System-ID)</li>
<li>Erstellt Marker (Leaflet)</li>
<li>Rückgabe über Callback <code>setMarkersFunction(markers)</code></li>
</ul></li>
<li>Die Marker werden per <code>setMarkerStates()</code>
gespeichert.</li>
<li>In MapComponent.js können sie aus
<code>markerStates[SystemName]</code> gelesen werden.</li>
<li>Sichtbarkeit wird über Redux (<code>mapLayersVisibility</code>)
gesteuert.</li>
<li>Überlappende Marker werden über
<code>checkOverlappingMarkers()</code> + <code>plusRoundIcon</code> +
OMS angezeigt.</li>
</ol>
<hr />
<h2 id="marker-aufbau">📦 Marker-Aufbau</h2>
<h3 id="createandsetdevices.js"><code>createAndSetDevices.js</code></h3>
<ul>
<li><p>Filtert Stations (<code>Points</code>) aus
<code>selectGisStationsStaticDistrict</code></p></li>
<li><p>Erstellt für jede gültige Station einen Marker:</p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode js"><code class="sourceCode javascript"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> marker <span class="op">=</span> L<span class="op">.</span><span class="fu">marker</span>([station<span class="op">.</span><span class="at">X</span><span class="op">,</span> station<span class="op">.</span><span class="at">Y</span>]<span class="op">,</span> { <span class="op">...</span> })<span class="op">;</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>oms<span class="op">.</span><span class="fu">addMarker</span>(marker)<span class="op">;</span> <span class="co">// Spiderfier-fähig</span></span></code></pre></div></li>
<li><p>Fügt Marker zur richtigen <code>LayerGroup</code> hinzu</p></li>
<li><p>Gibt alle Marker über Callback zurück</p></li>
</ul>
<hr />
<h2 id="overlappingmarkerspiderfier-oms">🕷️ OverlappingMarkerSpiderfier
(OMS)</h2>
<ul>
<li><p>Initialisiert in <code>useInitializeMap(...)</code> in
<code>MapComponent.js</code></p></li>
<li><p>Wird an <code>useDynamicDeviceLayers</code> übergeben</p></li>
<li><p>Marker werden dort registriert:
<code>oms.addMarker(marker)</code></p></li>
<li><p>Bei Klick auf das PlusIcon:</p>
<div class="sourceCode" id="cb4"><pre
class="sourceCode js"><code class="sourceCode javascript"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>plusMarker<span class="op">.</span><span class="fu">on</span>(<span class="st">&quot;click&quot;</span><span class="op">,</span> () <span class="kw">=&gt;</span> oms<span class="op">.</span><span class="fu">spiderfy</span>(nearbyMarkers))<span class="op">;</span></span></code></pre></div></li>
</ul>
<hr />
<h2 id="mermaid-diagramm">📘 Mermaid-Diagramm</h2>
<pre class="mermaid"><code>flowchart TD
A1[MapComponent] --&gt; B1[useDynamicDeviceLayers]
B1 --&gt; C1[loop über GisSystemStatic]
C1 --&gt; D1[createAndSetDevices]
D1 --&gt; E1[Filter stations aus Redux]
E1 --&gt; F1[erstelle Marker /Leaflet]
F1 --&gt; G1[Marker in LayerGroup einfügen]
G1 --&gt; H1[setMarkerStates im Hook]
H1 --&gt; A2[markerStates zurück nach MapComponent]
A2 --&gt; I1[Map aktualisiert Marker]
A2 --&gt; I2[checkOverlappingMarkers mit OMS]</code></pre>
<hr />
<h2 id="vorteile-der-neuen-lösung">✅ Vorteile der neuen Lösung</h2>
<table>
<colgroup>
<col style="width: 19%" />
<col style="width: 80%" />
</colgroup>
<thead>
<tr>
<th>Vorteil</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td>🔄 Flexibel</td>
<td>Neue Geräte-Typen automatisch erkannt</td>
</tr>
<tr>
<td>📦 Kompakt</td>
<td>Kein <code>useState</code> oder <code>useLayerVisibility</code> mehr
nötig</td>
</tr>
<tr>
<td>🧠 Wartbar</td>
<td>Eine zentrale Logik statt doppelter Komponentenlogik</td>
</tr>
<tr>
<td>🕷️ Integriert</td>
<td>OMS funktioniert automatisch bei überlappenden Markern</td>
</tr>
</tbody>
</table>
<hr />
<p>Letztes Update: automatisch generiert mit ChatGPT (OpenAI)</p>
<hr />
<p><a href="README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,5 @@
<h1 id="übersicht-docsarchitecture">📄 Übersicht: docs/architecture</h1>
<ul>
<li><a
href="device-layer-connection.md">device-layer-connection</a></li>
</ul>

View File

@@ -0,0 +1,393 @@
<!-- /docs/architecture.md -->
<h1 id="architekturübersicht-nodemap">🧠 Architekturübersicht
NodeMap</h1>
<p>Dieses Dokument beschreibt die technische Gesamtarchitektur des
Projekts <strong>NodeMap</strong>, einer kartenbasierten Webanwendung
zur Anzeige, Bearbeitung und Verwaltung von GIS-Daten, POIs und
Gerätestatus.</p>
<hr />
<h2 id="technologie-stack">⚙️ Technologie-Stack</h2>
<table>
<colgroup>
<col style="width: 23%" />
<col style="width: 76%" />
</colgroup>
<thead>
<tr>
<th>Komponente</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Frontend</strong></td>
<td>React 18 + Next.js (App Router)</td>
</tr>
<tr>
<td><strong>State-Management</strong></td>
<td>Redux Toolkit mit zentralem Store, Thunks &amp; Slices</td>
</tr>
<tr>
<td><strong>UI</strong></td>
<td>Tailwind CSS + Leaflet + React-Icons</td>
</tr>
<tr>
<td><strong>Backend-Anbindung</strong></td>
<td>Webservices via <code>WebServiceMap.asmx</code> (IIS) + lokale
Next.js API für DB</td>
</tr>
<tr>
<td><strong>Datenbank</strong></td>
<td>MySQL (Produktiv &amp; Entwicklung, z.T. via Docker)</td>
</tr>
<tr>
<td><strong>Deployment</strong></td>
<td>Windows Server (IIS), optional per <code>nssm</code> als Dienst</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="systemübersicht-ablauf">🔄 Systemübersicht (Ablauf)</h2>
<pre class="mermaid"><code>sequenceDiagram
participant Browser
participant TALASweb
participant NodeMap
participant MySQL
Browser-&gt;&gt;TALASweb: mapTypC.aspx?m=12&amp;u=484
TALASweb--&gt;&gt;Browser: iFrame lädt NodeMap
Browser-&gt;&gt;NodeMap: Liest m &amp; u aus URL
NodeMap-&gt;&gt;TALASweb: WebService-Requests (5 APIs)
NodeMap-&gt;&gt;MySQL: API-Anfragen zu POIs, Geräten, Linien
NodeMap--&gt;&gt;Browser: Interaktive Karte anzeigen
</code></pre>
<hr />
<h2 id="architekturüberblick">🗺️ Architekturüberblick</h2>
<pre><code>+------------------+ +------------------+ +------------------+
| Leaflet Map | &lt;---&gt; | Redux Store | &lt;---&gt; | Webservices |
| (Interaktivität) | | (Status &amp; Data) | | (IIS, .asmx) |
+------------------+ +------------------+ +------------------+
^
|
v
+------------------+ +------------------+ +-------------------+
| POI-Komponenten | &lt;---&gt; | Redux Slices | &lt;---&gt; | Next.js API-Routen|
| (Add/Edit) | | (z.B. poiSlice) | | (Datenbank) |
+------------------+ +------------------+ +-------------------+</code></pre>
<hr />
<h2 id="datenfluss-beispiel-poi-anzeigen">🔁 Datenfluss (Beispiel: POI
anzeigen)</h2>
<ol type="1">
<li>Leaflet-Karte lädt bei <code>MapComponent</code> Mounting</li>
<li>Redux-Thunk <code>fetchPoiMarkersThunk</code> wird ausgelöst</li>
<li>Thunk ruft <code>fetchPoiDataService.js</code> (DB) oder Webservice
(IIS) auf</li>
<li>Ergebnisse werden im Slice <code>readPoiMarkersStoreSlice</code>
gespeichert</li>
<li>Komponenten lesen POI-Daten über <code>useSelector(...)</code> aus
dem Store</li>
<li>POIs werden als Marker in Leaflet gesetzt</li>
</ol>
<hr />
<h2 id="schlüsselfunktionen-module">📁 Schlüsselfunktionen &amp;
Module</h2>
<table>
<colgroup>
<col style="width: 11%" />
<col style="width: 54%" />
<col style="width: 34%" />
</colgroup>
<thead>
<tr>
<th>Bereich</th>
<th>Datei/Modul</th>
<th>Aufgabe</th>
</tr>
</thead>
<tbody>
<tr>
<td>Kartenlogik</td>
<td><code>MapComponent.js</code></td>
<td>Zentrale Initialisierung und Layer-Logik</td>
</tr>
<tr>
<td>Webservices</td>
<td><code>services/webservice/</code></td>
<td>Kommunikation mit TALAS V5 Webservice</td>
</tr>
<tr>
<td>Datenbank</td>
<td><code>services/database/</code></td>
<td>Zugriff auf lokale Next.js-API &amp; DB</td>
</tr>
<tr>
<td>POIs</td>
<td><code>AddPOIModal.js</code>, <code>PoiUpdateModal.js</code></td>
<td>UI für POI-Erstellung &amp; -Bearbeitung</td>
</tr>
<tr>
<td>Redux</td>
<td><code>redux/slices/</code>, <code>redux/thunks/</code>,
<code>redux/store</code></td>
<td>Globaler State, API-Steuerung</td>
</tr>
<tr>
<td>Konfiguration</td>
<td><code>.env.development</code>,<code>.env.production</code>,
<code>config.js</code>, dynamic URLs</td>
<td>IP, basePath, Ports</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="besonderheiten">🧩 Besonderheiten</h2>
<ul>
<li><p><strong>Konfigurierbarer basePath:</strong></p></li>
<li><p><strong>Konfigurierbarer basePath:</strong><br />
Pfad wie <code>/talas5</code> ist optional und wird jetzt in
<code>public/config.json</code> als <code>basePath</code> gepflegt
werden.<br />
Die Konfiguration erfolgt je nach Umgebung über:</p>
<ul>
<li><code>.env.development</code> für lokale Entwicklung</li>
<li><code>.env.production</code> für produktiven Einsatz</li>
</ul></li>
<li><p><strong>Rechteabhängige UI:</strong><br />
Funktionen (z.B. POI bearbeiten) basieren auf Benutzerrechten
(<code>IdRight</code>) vom Server.</p></li>
<li><p><strong>Zentrale Komponentensteuerung:</strong><br />
Komponenten wie <code>MapLayersControlPanel</code> oder
<code>CoordinatePopup</code> kontrollieren Layer &amp;
Interaktion.</p></li>
<li><p><strong>Kontextmenü-Logik:</strong><br />
Marker &amp; Polylinien besitzen eigene Kontextmenüs dynamisch
zusammengesetzt und verwaltet.</p></li>
</ul>
<hr />
<h2 id="versionierung-builds">📦 Versionierung &amp; Builds</h2>
<ul>
<li>Version wird mit Husky Bibliothek automatisch erhöhert in
<code>scripts/bumpVersion.js</code></li>
</ul>
<hr />
<h2 id="weiterführende-dokumentation">📚 Weiterführende
Dokumentation</h2>
<ul>
<li><a
href="./build-and-deploy.md"><code>build-and-deploy.md</code></a></li>
<li><a
href="./env.local.schema.md"><code>env.local.schema.md</code></a></li>
<li><a href="./redux/slices/"><code>redux/slices/</code></a></li>
<li><a
href="./services/webservice/"><code>services/webservice/</code></a></li>
</ul>
<hr />
<h2 id="dynamische-layer-verwaltung-mit-redux">Dynamische
Layer-Verwaltung mit Redux</h2>
<pre class="mermaid"><code>flowchart TD
%% Webservice
subgraph Webservice
A1[GisSystemStatic API]
A2[GisStationsStaticDistrict API]
end
%% Redux
subgraph Redux
B1[fetchGisSystemStaticThunk]
B2[fetchGisStationsStaticDistrictThunk]
C1[&quot;gisSystemStaticSlice → selectGisSystemStatic&quot;]
C2[&quot;gisStationsStaticDistrictSlice → selectGisStationsStaticDistrict&quot;]
C3[&quot;mapLayersSlice → mapLayersVisibility&quot;]
end
%% React
subgraph React-Komponente
D1[MapComponent.js]
D2[&quot;useEffect: dynamische Layer&quot;]
D3[&quot;createAndSetDevices&quot;]
D4[&quot;layerRefs (useRef)&quot;]
D5[&quot;markerStates (useState)&quot;]
D6[&quot;useEffect: Sichtbarkeit prüfen&quot;]
D7[&quot;Map aktualisieren / add/remove&quot;]
D8[&quot;checkOverlappingMarkers&quot;]
end
%% Datenfluss
A1 --&gt; B1 --&gt; C1 --&gt; D2
A2 --&gt; B2 --&gt; C2 --&gt; D3
C3 --&gt; D6
D2 --&gt; D3
D3 --&gt; D4
D3 --&gt; D5
D5 --&gt; D6
D6 --&gt; D7
D6 --&gt; D8
D7 --&gt; D1
</code></pre>
<hr />
<p>Jetzt (dynamisch &amp; Redux-basiert): MapComponent.js ruft folgenden
Hook auf:</p>
<p>js Copy Edit const { markerStates, layerRefs } =
useDynamicDeviceLayers(map, GisSystemStatic, mapLayersVisibility,
priorityConfig, oms); useDynamicDeviceLayers.js verarbeitet die
GisSystemStatic-Liste:</p>
<p>Jedes System (z.B. “TALAS”, “ECI”, “Cisco”) bekommt einen eigenen
Marker-Layer.</p>
<p>Die Marker werden erstellt durch:</p>
<p>js Copy Edit createAndSetDevices(…) // Systemweise Marker erzeugen
createAndSetDevices.js:</p>
<p>Filtert alle Stations aus staticDistrictData, deren System ===
IdSystem.</p>
<p>Erstellt Marker für jedes Gerät.</p>
<p>Bindet Popup, Kontextmenü, Styling, Bounce usw.</p>
<p>Ruft setMarkersFunction(markers) auf → Übergibt die Marker zurück an
den Hook.</p>
<p>Der Hook speichert:</p>
<p>js Copy Edit setMarkerStates((prev) =&gt; ({ …prev, [Name]:
newMarkers })); MapComponent.js hat dann:</p>
<p>Zugriff auf alle Marker dynamisch über markerStates (ein Objekt mit
Schlüssel = Systemname)</p>
<p>Sichtbarkeit und OverlappingMarkerSpiderfier werden damit
verarbeitet.</p>
<hr />
<p>🔁 Die Geräte-Marker sind nicht mehr fest codiert, sondern werden
dynamisch erzeugt anhand der Webservice-Daten GisSystemStatic.</p>
<p>🔄 Sichtbarkeit (Checkbox im Control Panel) löst ein Event
visibilityChanged aus → MapComponent reagiert und rendert Marker
neu.</p>
<p>🕷️ Überlappende Marker werden mit checkOverlappingMarkers +
PlusRoundIcon verarbeitet.</p>
<pre class="mermaid"><code>flowchart TD
A1[MapComponent] --&gt; B1[useDynamicDeviceLayers]
B1 --&gt; C1[loop über GisSystemStatic]
C1 --&gt; D1[createAndSetDevices]
D1 --&gt; E1[Filter stations aus Redux]
E1 --&gt; F1[erstelle Marker /Leaflet]
F1 --&gt; G1[Marker in LayerGroup einfügen]
G1 --&gt; H1[setMarkerStates im Hook]
H1 --&gt; A2[markerStates zurück nach MapComponent]
A2 --&gt; I1[Map aktualisiert Marker]
A2 --&gt; I2[checkOverlappingMarkers mit OMS]</code></pre>
<hr />
<p>10.06.2025</p>
<h1 id="datenfluss-konzept-websocket-redux-ui">Datenfluss-Konzept:
WebSocket ↔︎ Redux ↔︎ UI</h1>
<p>Dieses Dokument beschreibt den technischen Ablauf des
Live-Datenflusses im NodeMap-Projekt, um neue Entwickler:innen beim
Onboarding zu unterstützen.</p>
<h2 id="ᵀᵃᵗᵉᵖᵏᴼᵏᴼᵉ-architekturübersicht">ᵀᵃᵗᵉᵖᵏᴼᵏᴼᵉ:
Architekturübersicht</h2>
<pre class="mermaid"><code>sequenceDiagram
autonumber
participant WS_Server as WebSocket Server
participant WebService as TALAS WebService
participant Browser
participant ReduxStore as Redux Store
participant UI as React Leaflet UI
loop Alle 5 Sekunden
WS_Server-&gt;&gt;WebService: fetch endpointX
alt Daten haben sich geändert
WS_Server--&gt;&gt;Browser: emit &#39;endpointXUpdated&#39;
Browser-&gt;&gt;ReduxStore: dispatch(fetchEndpointXThunk())
ReduxStore-&gt;&gt;WebService: fetch endpointX
WebService--&gt;&gt;ReduxStore: JSON response
ReduxStore--&gt;&gt;UI: update Slice
UI--&gt;&gt;User: re-render Markers
else Keine Änderung
WS_Server--&gt;&gt;WS_Server: keine Aktion
end
end</code></pre>
<h2 id="beteiligte-komponenten">Beteiligte Komponenten</h2>
<h3 id="websocket-server-server.js">WebSocket Server
(<code>server.js</code>)</h3>
<ul>
<li>Ruft regelmäßig (<code>setInterval</code>) die Webservice-Endpunkte
auf.</li>
<li>Erkennt Änderungen durch JSON-Vergleich
(<code>JSON.stringify</code>).</li>
<li>Sendet WebSocket-Events nur bei echten Änderungen.</li>
</ul>
<h3 id="client-mapcomponent">Client: MapComponent</h3>
<ul>
<li>Hört auf <code>socket.on("endpointXUpdated")</code>.</li>
<li>Ruft dann gezielt den passenden Redux-Thunk auf (z.B.
<code>fetchGisLinesStatusThunk</code>).</li>
</ul>
<h3 id="redux-store-thunks">Redux Store &amp; Thunks</h3>
<ul>
<li><p>Jeder Endpunkt besitzt:</p>
<ul>
<li>einen <code>Service</code> (API-Fetch)</li>
<li>einen <code>Thunk</code> (Redux-Logik)</li>
<li>einen <code>Slice</code> (State-Verwaltung)</li>
</ul></li>
</ul>
<h3 id="react-ui-leaflet-map">React UI (Leaflet Map)</h3>
<ul>
<li>Beobachtet relevante Redux-Slices via
<code>useSelector()</code>.</li>
<li>Aktualisiert Marker, Tooltip und Popup über
<code>createAndSetDevices()</code> und
<code>useDynamicDeviceLayers()</code>.</li>
</ul>
<h2 id="beispiel-endpunkte">Beispiel-Endpunkte</h2>
<table>
<colgroup>
<col style="width: 27%" />
<col style="width: 34%" />
<col style="width: 39%" />
</colgroup>
<thead>
<tr>
<th>Endpunktname</th>
<th>WebSocket Event</th>
<th>Redux Thunk</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>GisLinesStatus</code></td>
<td><code>GisLinesStatusUpdated</code></td>
<td><code>fetchGisLinesStatusThunk()</code></td>
</tr>
<tr>
<td><code>GisStationsMeasurements</code></td>
<td><code>GisStationsMeasurementsUpdated</code></td>
<td><code>fetchGisStationsMeasurementsThunk()</code></td>
</tr>
<tr>
<td><code>GisStationsStaticDistrict</code></td>
<td><code>GisStationsStaticDistrictUpdated</code></td>
<td><code>fetchGisStationsStaticDistrictThunk()</code></td>
</tr>
<tr>
<td><code>GisStationsStatusDistrict</code></td>
<td><code>GisStationsStatusDistrictUpdated</code></td>
<td><code>fetchGisStationsStatusDistrictThunk()</code></td>
</tr>
</tbody>
</table>
<h2 id="vorteile">Vorteile</h2>
<ul>
<li>UI aktualisiert sich nur bei echten Datenänderungen → weniger
Re-Renders.</li>
<li>Live-Synchronisation zwischen Datenquelle und Anzeige.</li>
<li>Skalierbar für beliebige Endpunkte.</li>
</ul>
<h2 id="todoerweiterungen">ToDo/Erweiterungen</h2>
<ul>
<li>Automatische Reconnect-Logik für WebSocket.</li>
<li>Anzeige des letzten Update-Zeitpunkts in UI.</li>
<li>Logging-UI für WebSocket-Messages zur Diagnose.</li>
</ul>
<hr />
<blockquote>
<p>Letzte Änderung: <code>{{heutiges Datum}}</code> von Ismail Ali</p>
</blockquote>
<hr />
<p><a href="README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,19 @@
<h1 id="architektur-verbindung-gis-system-gis-station">📡 Architektur:
Verbindung GIS-System &amp; GIS-Station</h1>
<p>Dieses Diagramm zeigt den Ablauf, wie Geräte (Marker) auf der Karte
über die ID (System gegen IdSystem) korrekt geladen und sichtbar gemacht
werden.</p>
<pre class="mermaid"><code>flowchart TD
A[Stationen aus GIS Stations District mit System ID zum Beispiel 111] --&gt; B[useDynamicDeviceLayers.js]
B --&gt; C[Filter und Gruppierung nach System ID]
C --&gt; D[createAndSetDevices.js erzeugt Marker]
D --&gt; E[MapComponent.js zeigt Marker auf Karte]
subgraph Redux
F[fetchGisSystemStaticService.js liefert Systeme mit IdSystem]
F --&gt; G[fetchGisSystemStaticThunk.js]
G --&gt; H[setInitialLayers mit system-IdSystem]
H --&gt; I[mapLayersSlice.js speichert Sichtbarkeit]
end
I --&gt;|Sichtbarkeit steuert Anzeige| E</code></pre>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,100 @@
<!-- /docs/build-amddeploy.md -->
<h1 id="deployment-build-verhalten-next.js">🛠 Deployment &amp;
Build-Verhalten (Next.js)</h1>
<p>Diese Datei beschreibt, welche Projektdateien in den Build
(<code>.next/</code>) aufgenommen werden und welche nicht.<br />
Ziel: Klarheit für Onboarding, Deployment-ZIP-Erstellung oder CI/CD.</p>
<hr />
<h2 id="wird-beim-npm-run-build-in-.next-gespeichert">📦 Wird beim
<code>npm run build</code> in <code>.next/</code> gespeichert</h2>
<table>
<colgroup>
<col style="width: 34%" />
<col style="width: 65%" />
</colgroup>
<thead>
<tr>
<th>Inhalt</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td>Kompilierte Seiten</td>
<td>Alle unter <code>/pages/</code></td>
</tr>
<tr>
<td>API-Routen</td>
<td>Alles aus <code>pages/api/</code></td>
</tr>
<tr>
<td>Assets aus <code>public/</code></td>
<td>Werden im Build nicht verändert, aber ausgeliefert</td>
</tr>
<tr>
<td>CSS-Dateien (Tailwind)</td>
<td>Werden gebundelt und minimiert</td>
</tr>
<tr>
<td><code>.env.production</code> / <code>.env.development</code></td>
<td>Umgebungsabhängige Konfiguration. Wird eingelesen, aber nicht
exportiert</td>
</tr>
</tbody>
</table>
<div class="line-block">JS/TS-Quellcode | Wird zu Client- und
Server-Bundles kompiliert |</div>
<hr />
<h2 id="wird-nicht-in-.next-aufgenommen">🧹 Wird <strong>nicht</strong>
in <code>.next/</code> aufgenommen</h2>
<table>
<colgroup>
<col style="width: 38%" />
<col style="width: 61%" />
</colgroup>
<thead>
<tr>
<th>Ordner/Datei</th>
<th>Zweck / Grund</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>__tests__</code>, <code>__mocks__</code></td>
<td>Nur lokal für Tests, nicht im Build</td>
</tr>
<tr>
<td><code>cypress/</code></td>
<td>End-to-End-Tests, nur für lokale Entwicklung</td>
</tr>
<tr>
<td><code>scripts/</code></td>
<td>Hilfsskripte, nicht für Runtime relevant</td>
</tr>
<tr>
<td><code>docs/</code></td>
<td>Dokumentation, nur für Entwickler</td>
</tr>
<tr>
<td><code>README.md</code>, <code>CHANGELOG.md</code></td>
<td>Doku nicht erforderlich zur Laufzeit</td>
</tr>
<tr>
<td><code>Jenkinsfile</code>, <code>.github/</code></td>
<td>CI/CD wird vom Buildsystem verwendet</td>
</tr>
</tbody>
</table>
<hr />
<h2
id="empfohlene-struktur-für-deployment-z.-b.-zip-upload-auf-server">📂
Empfohlene Struktur für Deployment (z.B. ZIP-Upload auf Server)</h2>
<p>Nur folgende Dateien/Ordner übertragen:</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">.next/</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ex">public/</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="ex">package.json</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="ex">package-lock.json</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="ex">.env.production</span></span></code></pre></div>
<hr />
<p><a href="README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,51 @@
<!-- /docs/checklist.md -->
<h1 id="projektpflege-checkliste">🧾 Projektpflege-Checkliste</h1>
<p>Diese Datei dient als persönliche Gedächtnisstütze bei der
Entwicklung und Pflege des Projekts.</p>
<p>Bevor du einen Feature-, Refactor- oder Bugfix-Commit abschließt, geh
diese Liste durch:</p>
<hr />
<h2 id="dokumentation">📝 Dokumentation</h2>
<ul class="task-list">
<li><label><input type="checkbox" />Ist <code>README.md</code> noch
aktuell (Projektziel, Setup, Nutzung)?</label></li>
<li><label><input type="checkbox" />Wurde <code>CHANGELOG.md</code>
ergänzt (mit Datum, Version, Änderung)?</label></li>
<li><label><input type="checkbox" />Wurde ggf. ein neuer Punkt in
<code>/docs/</code> ergänzt oder aktualisiert?</label></li>
<li><label><input type="checkbox" />Sind Beispiel-URLs oder sensible
Daten <strong>nicht im Code</strong>, sondern dokumentiert?</label></li>
</ul>
<hr />
<h2 id="konfiguration">📦 Konfiguration</h2>
<ul class="task-list">
<li><label><input type="checkbox" />Sind <code>.env.production</code>
und <code>.env.development</code> aktuell und vollständig?</label></li>
<li><label><input type="checkbox" />Wird jede Konfiguration
<strong>ausschließlich über <code>.env.*</code> Dateien</strong>
gesteuert?</label></li>
</ul>
<hr />
<h2 id="codequalität-git">✅ Codequalität &amp; Git</h2>
<ul class="task-list">
<li><label><input type="checkbox" />Ist die Git-Commit-Message
beschreibend und lesbar (z.B. <code>feat:</code>, <code>fix:</code>,
<code>docs:</code>)?</label></li>
<li><label><input type="checkbox" />Wurden unnötige Debug-Logs entfernt
oder per <code>NODE_ENV</code> abgesichert?</label></li>
<li><label><input type="checkbox" />Wurden Änderungen getestet (lokal,
ggf. auf Testsystem)?</label></li>
</ul>
<hr />
<h2 id="onboarding-freundlich">🧭 Onboarding-freundlich?</h2>
<ul class="task-list">
<li><label><input type="checkbox" />Könnte ein neuer Entwickler mit den
aktuellen Dokumenten verstehen, was wie funktioniert?</label></li>
<li><label><input type="checkbox" />Gibt es Hinweise zur Architektur,
API-Flows oder Besonderheiten im Code?</label></li>
</ul>
<hr />
<p>Du kannst diese Checkliste in jedem Projekt beibehalten und auf deine
Arbeitsweise anpassen.</p>
<hr />
<p><a href="README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,67 @@
<!-- /docs/components/README.md -->
<h1 id="components-übersicht-über-alle-ui-komponenten">🧩
<code>components/</code> Übersicht über alle UI-Komponenten</h1>
<p>Dieses Verzeichnis enthält die gesamten React-Komponenten der
TALAS-Kartenanwendung.<br />
Sie sind thematisch gegliedert in Teilbereiche für Kontextmenüs, POIs,
Polylinien, Modale und die zentrale <code>MapComponent</code>.</p>
<hr />
<h2 id="strukturübersicht">📁 Strukturübersicht</h2>
<div class="sourceCode" id="cb1"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">components/</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ex">├──</span> contextmenu/ <span class="co"># Komponenten für rechte Maustaste &amp; Kontextaktionen</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> ├── CoordinatePopup.js</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> └── useMapContextMenu.js</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="ex">├──</span> gisPolylines/ <span class="co"># Polylinien (Kabelstrecken)</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> ├── PolylineContextMenu.js</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> └── icons/</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> ├── CircleIcon.js</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> ├── EndIcon.js</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> ├── StartIcon.js</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> └── SupportPointIcons.js</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="ex">├──</span> icons/devices/overlapping/ <span class="co"># Zusätzliche Overlap-Icons für Geräte</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> └── PlusRoundIcon.js</span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a><span class="ex">├──</span> mainComponent/ <span class="co"># Hauptkomponenten für Karteninitialisierung</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> ├── MapComponent.js</span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> └── hooks/</span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> └── useInitializeMap.js</span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a><span class="ex">├──</span> pois/ <span class="co"># POI-spezifische Modale</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> ├── AddPOIModal.js</span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> └── PoiUpdateModal.js</span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a><span class="ex">├──</span> uiWidgets/ <span class="co"># UI-Widgets</span></span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> ├── CoordinateInput.js</span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> ├── VersionInfoModal.js</span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> ├── TestScript.js</span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> └── mapLayersControlPanel/</span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> ├── EditModeToggle.js</span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a><span class="ex"></span> └── MapLayersControlPanel.js</span></code></pre></div>
<hr />
<h2 id="beschreibung-der-hauptbereiche">🔎 Beschreibung der
Hauptbereiche</h2>
<h3 id="contextmenu"><code>contextmenu/</code></h3>
<p>Rechtsklick-Menüs für Marker, POIs, Polylinien. Steuert Anzeige &amp;
Verhalten.</p>
<h3 id="gispolylines"><code>gisPolylines/</code></h3>
<p>Komponenten für das Zeichnen, Bearbeiten und Interagieren mit
Linien/Strecken.</p>
<h3 id="maincomponent"><code>mainComponent/</code></h3>
<p>Zentrale Leaflet-Map-Logik &amp; Initialisierung via
<code>MapComponent</code> und <code>useInitializeMap</code>.</p>
<h3 id="pois"><code>pois/</code></h3>
<p>Modale für das Hinzufügen und Bearbeiten von POIs (Points of
Interest).</p>
<h3 id="uiwidgets"><code>uiWidgets/</code></h3>
<p>Komponenten wie Eingabefelder für Koordinaten-Suche, Infoboxen und
Control Panel für Geräte Layers .</p>
<hr />
<h2 id="besonderheiten">✅ Besonderheiten</h2>
<ul>
<li>Verwendet <strong>Tailwind CSS</strong> für Styling</li>
<li>Integration mit Redux, Leaflet, OverlappingMarkerSpiderfier</li>
<li>Vollständig modular &amp; testbar aufgebaut</li>
</ul>

View File

@@ -0,0 +1,59 @@
<!-- /docs/components/TestScript.md -->
<h1 id="testscript.js">🧪 TestScript.js</h1>
<p>Ein einfaches React-Testskript zur Laufzeitüberprüfung von
Codefragmenten in <code>setupPolylines.js</code>.</p>
<h2 id="zweck">Zweck</h2>
<p>Dieses Skript durchsucht die geladene
<code>setupPolylines.js</code>-Datei (per <code>raw-loader</code>) nach
bestimmten Kontextmenüeinträgen:</p>
<ul>
<li>„Stützpunkt entfernen“</li>
<li>„Stützpunkt hinzufügen“</li>
</ul>
<h2 id="vorgehen">Vorgehen</h2>
<ul>
<li>Lädt <code>setupPolylines.js</code> als Text via
<code>!!raw-loader!</code></li>
<li>Nutzt reguläre Ausdrücke zur Prüfung</li>
<li>Gibt Ergebnisse farblich formatiert in der Konsole aus</li>
</ul>
<h2 id="ausgaben">Ausgaben</h2>
<table>
<thead>
<tr>
<th>Zustand</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td>✅ Test bestanden</td>
<td>Der gesuchte Text wurde gefunden</td>
</tr>
<tr>
<td>❌ Test fehlgeschlagen</td>
<td>Der gesuchte Text fehlt in der Datei</td>
</tr>
<tr>
<td> Info</td>
<td>Neutrale Zusatzinformationen in der Konsole</td>
</tr>
</tbody>
</table>
<h2 id="besonderheiten">Besonderheiten</h2>
<ul>
<li>Kein visuelles UI Rückmeldung nur über
<code>console.log</code></li>
<li>Eignet sich als Dev-Hilfe für Refactoring oder PR-Checks</li>
</ul>
<h2 id="beispielausgabe">Beispielausgabe</h2>
<pre class="plaintext"><code>✔ Test bestanden: Der Text für &#39;Stützpunkt entfernen&#39; wurde gefunden.
Info: Überprüfung abgeschlossen.</code></pre>
<h2 id="hinweise">Hinweise</h2>
<ul>
<li>Wird automatisch beim Mount (via <code>useEffect</code>)
ausgeführt</li>
<li><code>return null</code> → keine sichtbare Ausgabe</li>
</ul>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,61 @@
<!-- /docs/components/contextmenu/CoordinatePopup.md -->
<h1 id="coordinatepopup.js">📌 CoordinatePopup.js</h1>
<p>Zeigt ein modales Fenster mit Koordinateninformationen an, z.B. aus
einem Kontextmenü heraus.</p>
<figure>
<img src="../../screenshots/CoordinatePopup.png"
alt="CoordinatePopup" />
<figcaption aria-hidden="true">CoordinatePopup</figcaption>
</figure>
<h2 id="features">Features</h2>
<ul>
<li>Darstellung eines Koordinatenwerts (<code>lat,lng</code>)</li>
<li>Kopieren in die Zwischenablage (Clipboard API + Fallback)</li>
<li>Modal zentriert mit Tailwind CSS</li>
<li>Zwei Buttons: „Kopieren“ und „Schließen“</li>
</ul>
<h2 id="props">Props</h2>
<table>
<colgroup>
<col style="width: 19%" />
<col style="width: 14%" />
<col style="width: 65%" />
</colgroup>
<thead>
<tr>
<th>Name</th>
<th>Typ</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>isOpen</code></td>
<td><code>boolean</code></td>
<td>Steuert Sichtbarkeit des Modals</td>
</tr>
<tr>
<td><code>coordinates</code></td>
<td><code>string</code></td>
<td>Zu zeigende Koordinaten (z.B. <code>"53.2,8.1"</code>)</td>
</tr>
<tr>
<td><code>onClose</code></td>
<td><code>function</code></td>
<td>Wird bei Klick auf „Schließen“ ausgelöst</td>
</tr>
</tbody>
</table>
<h2 id="design">Design</h2>
<ul>
<li>Tailwind-Klassen für zentriertes Layout (<code>fixed</code>,
<code>inset-0</code>, <code>z-50</code>)</li>
<li>Leicht animierter Button-Hover</li>
</ul>
<h2 id="interne-logik">Interne Logik</h2>
<ul>
<li>Nutzt <code>navigator.clipboard.writeText</code> oder Fallback mit
<code>document.execCommand("copy")</code></li>
<li>Stoppt Event-Bubbling, um Klick außerhalb zu erkennen</li>
</ul>
<p>🔙 <a href="./README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,62 @@
<!-- /docs/components/contextmenu/README.md temp branch damit develop zurücksetzt-->
<h1 id="contextmenu-kontextmenü-komponenten">🖱️
<code>contextmenu/</code> Kontextmenü-Komponenten</h1>
<p>Dieses Verzeichnis enthält Komponenten und Hooks zur Anzeige und
Steuerung von Kontextmenüs in der Leaflet-Kartenanwendung. Sie dienen
der Interaktion mit POIs, Koordinaten und Layer-Objekten per
Rechtsklick.</p>
<h2 id="usemapcontextmenu"><img
src="../../screenshots/useMapContextMenu.png"
alt="useMapContextMenu" /></h2>
<h2 id="enthaltene-dateien">📂 Enthaltene Dateien</h2>
<table>
<colgroup>
<col style="width: 40%" />
<col style="width: 59%" />
</colgroup>
<thead>
<tr>
<th>Datei</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><a
href="./CoordinatePopup.md"><code>CoordinatePopup.js</code></a></td>
<td>Zeigt ein kleines Kontextfenster mit Koordinaten und
Copy-Funktion</td>
</tr>
<tr>
<td><a
href="./useMapContextMenu.md"><code>useMapContextMenu.js</code></a></td>
<td>Hook zur Initialisierung und Verwaltung des Kontextmenüs auf der
Karte</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="verwendung">🔄 Verwendung</h2>
<p>Diese Komponenten sind typischerweise eingebunden in:</p>
<ul>
<li><a
href="../mainComponent/MapComponent.md"><code>MapComponent.js</code></a></li>
<li><a
href="../gisPolylines/PolylineContextMenu.md"><code>PolylineContextMenu.js</code></a></li>
<li>Marker- und Linienfunktionen aus <code>setupDevices</code>,
<code>setupPolylines</code></li>
</ul>
<hr />
<h2 id="ziel">🎯 Ziel</h2>
<p>Ermöglicht einfache Benutzerinteraktion mit:</p>
<ul>
<li>Geräten</li>
<li>Koordinaten</li>
<li>POIs</li>
<li>Streckenabschnitten</li>
</ul>
<hr />
<h2 id="weitere-dokumentation">📚 Weitere Dokumentation</h2>
<p>Alle Markdown-Dateien für Komponenten befinden sich im
<code>/docs/components/contextmenu/</code> Verzeichnis.</p>
<p>🔙 <a href="../README.md">Zurück zu <code>components</code></a></p>

View File

@@ -0,0 +1,67 @@
<!-- /docs/components/contextmenu/useMapContextMenu.md -->
<h1 id="usemapcontextmenu.js">🖱️ useMapContextMenu.js</h1>
<p>Initialisiert Kontextmenüeinträge für die Leaflet-Karte.<br />
Wird typischerweise in <code>initializeMap()</code> oder
<code>MapComponent</code> verwendet.</p>
<figure>
<img src="../../screenshots/useMapContextMenu.png"
alt="useMapContextMenu" />
<figcaption aria-hidden="true">useMapContextMenu</figcaption>
</figure>
<h2 id="kontextmenüeinträge">Kontextmenüeinträge</h2>
<table>
<colgroup>
<col style="width: 29%" />
<col style="width: 70%" />
</colgroup>
<thead>
<tr>
<th>Eintrag</th>
<th>Funktion</th>
</tr>
</thead>
<tbody>
<tr>
<td>Koordinaten anzeigen</td>
<td>Öffnet <code>CoordinatePopup</code> mit aktueller Position</td>
</tr>
<tr>
<td>Reinzoomen</td>
<td>Zoomt 3 Stufen näher an das Zentrum heran</td>
</tr>
<tr>
<td>Rauszoomen</td>
<td>Zoomt 3 Stufen heraus</td>
</tr>
<tr>
<td>Hier zentrieren</td>
<td>Verschiebt Kartenzentrum auf Klickposition</td>
</tr>
<tr>
<td>POI hinzufügen</td>
<td>(nur bei <code>editMode=true</code>) öffnet POI-Dialog</td>
</tr>
</tbody>
</table>
<h2 id="parameter">Parameter</h2>
<div class="sourceCode" id="cb1"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">addItemsToMapContextMenu</span>(</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> map<span class="op">,</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> menuItemAdded<span class="op">,</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> setMenuItemAdded<span class="op">,</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> setShowCoordinatesModal<span class="op">,</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> setShowPoiModal<span class="op">,</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> setPopupCoordinates<span class="op">,</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> openPopupWithCoordinates</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>)<span class="op">;</span></span></code></pre></div>
<h2 id="besonderheiten">Besonderheiten</h2>
<ul>
<li><p>Prüft auf <code>localStorage.editMode</code> für
POI-Eintrag</p></li>
<li><p>FlyTo-Animationen für Zoom-Vorgänge mit dynamischer
Dauer</p></li>
<li><p>Modularer Aufbau: <code>openPopupWithCoordinates</code> wird
extern übergeben</p>
<p>🔙 <a href="./README.md">Zurück zu contextmenu</a></p></li>
</ul>
<hr />
<p><a href="../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,67 @@
<!-- /docs/components/gisPolylines/PolylineContextMenu.md -->
<h1 id="polylinecontextmenu.js">📐 PolylineContextMenu.js</h1>
<p>Ein einfaches benutzerdefiniertes Kontextmenü zur Interaktion mit
Linien (Polylinien) auf der Karte.</p>
<figure>
<img src="../../screenshots/PolylineContextMenu.png"
alt="GIS Ployline contextmenu" />
<figcaption aria-hidden="true">GIS Ployline contextmenu</figcaption>
</figure>
<h2 id="zweck">Zweck</h2>
<p>Das Menü erlaubt folgende Interaktionen:</p>
<ul>
<li> „Stützpunkt hinzufügen“</li>
<li> „Stützpunkt entfernen“</li>
<li>❌ „Schließen“</li>
</ul>
<p>Wird dynamisch positioniert anhand der Klickkoordinaten
(<code>position.x</code>, <code>position.y</code>).</p>
<h2 id="props">Props</h2>
<table>
<colgroup>
<col style="width: 19%" />
<col style="width: 12%" />
<col style="width: 67%" />
</colgroup>
<thead>
<tr>
<th>Prop</th>
<th>Typ</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>position</code></td>
<td><code>{x, y}</code></td>
<td>Position in Pixelkoordinaten (z.B. von Mausereignis)</td>
</tr>
<tr>
<td><code>onAddPoint</code></td>
<td><code>function</code></td>
<td>Handler für „Stützpunkt hinzufügen“</td>
</tr>
<tr>
<td><code>onRemovePoint</code></td>
<td><code>function</code></td>
<td>Handler für „Stützpunkt entfernen“</td>
</tr>
<tr>
<td><code>onClose</code></td>
<td><code>function</code></td>
<td>Handler zum Schließen des Menüs</td>
</tr>
</tbody>
</table>
<h2 id="styling">Styling</h2>
<ul>
<li>Absolut positioniertes <code>div</code></li>
<li>Weißer Hintergrund, schwarzer Rahmen</li>
<li>Kein Tailwind purer Inline-Style</li>
</ul>
<h2 id="verwendung">Verwendung</h2>
<p>Eingebettet z.B. in <code>setupPolylines.js</code> oder
<code>PolylineLayerManager</code>, um rechte Mausklicks auf Linien zu
behandeln.</p>
<hr />
<p><a href="../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,164 @@
<!-- /docs/components/gisPolylines/README.md -->
<h1 id="übersicht-docscomponentsgispolylines">📄 Übersicht:
docs/components/gisPolylines</h1>
<p>Diese Komponente verwaltet die Darstellung und Interaktion von
GIS-Polylinien in der Leaflet-Karte.<br />
Sie kombiniert Statusdaten, statische Linien und Stationsdaten zu
Tooltips und Farben.</p>
<hr />
<h2 id="data-flow-diagram">🧬 Data Flow Diagram</h2>
<p>Dieses Diagramm zeigt den <strong>Datenfluss</strong> zwischen den
Redux-Slices, Thunks, Service-Funktionen und den
React-Komponenten,<br />
die für die Anzeige der <strong>GIS-Polylinien</strong> zuständig
sind.</p>
<h3 id="ablauf-erklärt">📖 Ablauf erklärt</h3>
<ol type="1">
<li><strong>Beim Laden der Seite</strong> ruft die Hook
<code>useLineData</code> mehrere Thunks auf:
<ul>
<li>Diese laden Linien, Statusdaten und Stationsdaten über entsprechende
Service-Funktionen.</li>
</ul></li>
<li>Die Thunks speichern die geladenen Daten in Redux-Slices.</li>
<li><code>useLineData</code> liest diese Redux-Daten aus und kombiniert
sie:
<ul>
<li>Zuordnung nach <code>idLD</code> und <code>Modul</code></li>
</ul></li>
<li>Daraus entsteht:
<ul>
<li>Eine Prioritätsfarbe für die Linie</li>
<li>Ein Tooltip-HTML mit Modulname, Slot, Station (LD_Name) und
Statusmeldungen.</li>
</ul></li>
<li>Diese Daten werden weitergegeben an:
<ul>
<li><code>generateLineTooltipContent</code> → für Tooltips bei
Hover</li>
<li><code>PolylineContextMenu</code> → für Kontextmenü bei
Rechtsklick</li>
<li>Leaflet Polyline-Komponente → für farbige Darstellung</li>
</ul></li>
</ol>
<pre class="mermaid"><code>graph TD
subgraph Redux Store
A1[gisLinesFromDatabase Slice]
A2[gisLinesStatusFromWebservice Slice]
A3[gisStationsStaticDistrict Slice]
end
subgraph Thunks
T1[fetchGisLinesThunk]
T2[fetchGisLinesStatusThunk]
T3[fetchGisStationsStaticDistrictThunk]
end
subgraph Services
S1[fetchGisLinesService]
S2[fetchGisLinesStatusService]
S3[fetchGisStationsStaticDistrictService]
end
subgraph Komponenten
C1[useLineData Hook]
C2[generateLineTooltipContent]
C3[PolylineContextMenu.js]
C4[Leaflet Polyline Rendering]
end
T1 --&gt; S1
T2 --&gt; S2
T3 --&gt; S3
T1 --&gt; A1
T2 --&gt; A2
T3 --&gt; A3
A1 --&gt; C1
A2 --&gt; C1
A3 --&gt; C1
C1 --&gt; C2
C2 --&gt; C4
C1 --&gt; C3</code></pre>
<hr />
<h2 id="wichtige-dateien">📦 Wichtige Dateien</h2>
<table>
<colgroup>
<col style="width: 51%" />
<col style="width: 48%" />
</colgroup>
<thead>
<tr>
<th>Datei</th>
<th>Zweck</th>
</tr>
</thead>
<tbody>
<tr>
<td><a
href="tooltip/useLineData.js"><code>useLineData.js</code></a></td>
<td>Holt Linien-, Status- und Stationsdaten aus Redux und kombiniert
sie</td>
</tr>
<tr>
<td><a
href="tooltip/generateLineTooltipContent.js"><code>generateLineTooltipContent.js</code></a></td>
<td>Baut HTML für die Tooltips</td>
</tr>
<tr>
<td><a
href="PolylineContextMenu.md"><code>PolylineContextMenu.js</code></a></td>
<td>Kontextmenü zur Interaktion mit Linien</td>
</tr>
</tbody>
</table>
<hr />
<figure>
<img src="../../screenshots/gisPolylines.png" alt="GIS Polylines" />
<figcaption aria-hidden="true">GIS Polylines</figcaption>
</figure>
<hr />
<p><a href="../../README.md">Zurück zur Übersicht</a></p>
<hr />
<h2 id="technischer-hintergrund-für-einsteiger">📘 Technischer
Hintergrund für Einsteiger</h2>
<p>Diese Komponente verbindet Daten aus <strong>zwei unterschiedlichen
Systemen</strong>:</p>
<ol type="1">
<li><strong>NodeMap App (Next.js API)</strong><br />
→ liefert die <strong>Geometrie der Linien</strong> direkt aus der
Datenbank (ohne Webservice).</li>
<li><strong>TALAS.web WebService</strong><br />
→ liefert <strong>Statusinformationen und Stationsnamen</strong>
(LD_Name).</li>
</ol>
<h3 id="ablauf-im-detail">🔄 Ablauf im Detail</h3>
<ul>
<li><strong>Liniengeometrie</strong> (<code>idLD</code>,
<code>idModul</code>, <code>points</code>) kommt über
<code>fetchGisLinesThunk</code> aus der Datenbank.</li>
<li><strong>Statusinformationen</strong> (Meldungen, Farben, Modulname,
Slot) kommen über <code>fetchGisLinesStatusThunk</code>.</li>
<li><strong>Stationsnamen</strong> (LD_Name) kommen über
<code>fetchGisStationsStaticDistrictThunk</code>.</li>
<li>Die Hook <code>useLineData.js</code> verbindet alle Infos → erzeugt
Tooltip-HTML &amp; Farblogik.</li>
<li><code>generateLineTooltipContent.js</code> erstellt den konkreten
Tooltip-HTML-String.</li>
</ul>
<h3 id="wichtig-für-debugging">🧠 Wichtig für Debugging</h3>
<ul>
<li><strong>Zuordnung</strong> erfolgt immer über <code>idLD</code> und
<code>Modul</code>.</li>
<li>Stationen findest du im Slice
<code>gisStationsStaticDistrict.Points[] → LD_Name</code></li>
<li>Linien findest du im Slice <code>gisLinesFromDatabase</code></li>
<li>Statusinfos findest du im Slice
<code>gisLinesStatusFromWebservice</code></li>
</ul>
<p>🛠 <strong>Fehler wie “Station: N/A”</strong> entstehen, wenn
<code>idLD</code> im Status vorhanden ist, aber keine passende Station
in <code>gisStationsStaticDistrict</code> gefunden wurde.</p>

View File

@@ -0,0 +1,19 @@
<!-- /docs/components/gisPolylines/icons/CircleIcon.md -->
<h1 id="circleicon.js">🔘 CircleIcon.js</h1>
<p>Ein einfacher, grauer runder Marker als Stützpunkt in einer
Polyline.</p>
<figure>
<img src="../../../screenshots/CircleIcon.png" alt="CircleIcon" />
<figcaption aria-hidden="true">CircleIcon</figcaption>
</figure>
<h2 id="eigenschaften">Eigenschaften</h2>
<ul>
<li>Stil: grauer Kreis mit schwarzem Rand</li>
<li>Größe: 10×10px, IconSize 25×25px (wegen Klickfläche)</li>
<li>Klasse: <code>custom-circle-icon</code></li>
</ul>
<h2 id="verwendung">Verwendung</h2>
<p>Wird in Polylinien als Zwischenpunkt gesetzt. Inaktiv, aber
sichtbar.</p>
<hr />
<p><a href="../../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,18 @@
<!-- /docs/components/gisPolylines/icons/EndIcon.md -->
<h1 id="endicon.js">🔲 EndIcon.js</h1>
<p>Ein Viereck zur Markierung des Endpunkts einer Polyline.</p>
<figure>
<img src="../../../screenshots/EndIcon.png" alt="EndIcon" />
<figcaption aria-hidden="true">EndIcon</figcaption>
</figure>
<h2 id="eigenschaften">Eigenschaften</h2>
<ul>
<li>Stil: graues Quadrat mit schwarzem Rand</li>
<li>Größe: 14×14px</li>
<li>Klasse: <code>custom-end-icon</code></li>
</ul>
<h2 id="verwendung">Verwendung</h2>
<p>Wird am letzten Punkt einer Linie gesetzt, z.B.
<code>lineData.coordinates[line.length - 1]</code></p>
<hr />
<p><a href="../../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,13 @@
<h1 id="übersicht-docscomponentsgispolylinesicons">📄 Übersicht:
docs/components/gisPolylines/icons</h1>
<ul>
<li><a href="CircleIcon.md">CircleIcon</a></li>
<li><a href="EndIcon.md">EndIcon</a></li>
<li><a href="StartIcon.md">StartIcon</a></li>
<li><a href="SupportPointIcons.md">SupportPointIcons</a></li>
</ul>
<figure>
<img src="../../../screenshots/gisPolylinesIcons.png"
alt="gisPolylinesIcons" />
<figcaption aria-hidden="true">gisPolylinesIcons</figcaption>
</figure>

View File

@@ -0,0 +1,17 @@
<!-- /docs/components/gisPolylines/icons/StartIcon.md -->
<h1 id="starticon.js">🔺 StartIcon.js</h1>
<p>Ein SVG-Dreieck zur Markierung des Startpunkts einer Polyline.</p>
<figure>
<img src="../../../screenshots/StartIcon.png" alt="StartIcon" />
<figcaption aria-hidden="true">StartIcon</figcaption>
</figure>
<h2 id="eigenschaften">Eigenschaften</h2>
<ul>
<li>Schwarzes Dreieck mit grauem Overlay (Polygon SVG)</li>
<li>Größe: 18×18px</li>
<li>Klasse: <code>custom-start-icon</code></li>
</ul>
<h2 id="verwendung">Verwendung</h2>
<p>Wird am ersten Punkt einer Polyline platziert.</p>
<hr />
<p><a href="../../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,26 @@
<!-- /docs/components/gisPolylines/icons/SupportPointIcons.md -->
<h1 id="supportpointicons.js"> SupportPointIcons.js</h1>
<p>Definiert zwei Icons für interaktive Stützpunkte in einer
Polyline:</p>
<figure>
<img src="../../../screenshots/CircleIcon.png" alt="CircleIcon" />
<figcaption aria-hidden="true">CircleIcon</figcaption>
</figure>
<h2 id="addsupportpointicon">AddSupportPointIcon</h2>
<ul>
<li>Grüner Kreis mit weißem Rand und Pluszeichen</li>
<li><code>iconSize</code>: 24×24px</li>
</ul>
<h2 id="removesupportpointicon">RemoveSupportPointIcon</h2>
<ul>
<li>Roter Kreis mit weißem Rand und Minuszeichen</li>
<li><code>iconSize</code>: 24×24px</li>
</ul>
<h2 id="verwendung">Verwendung</h2>
<ul>
<li>Hinzufügen/Entfernen von Zwischenpunkten in der Bearbeitungsansicht
(editMode)</li>
<li>Marker erscheinen z.B. bei Maus-Hover oder per Kontextmenü</li>
</ul>
<hr />
<p><a href="../../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,55 @@
<!-- /docs/components/icons/devices/overlapping/PlusRoundIcon.md -->
<h1 id="plusroundicon.js"> PlusRoundIcon.js</h1>
<p>Ein einfaches Leaflet-Icon, das ein rundes Pluszeichen
darstellt.<br />
Wird für zusätzliche UI-Markierungen auf Geräten oder überlappenden
Icons verwendet.</p>
<figure>
<img src="../../../../screenshots/PlusRoundIcon.png"
alt="PlusRoundIcon" />
<figcaption aria-hidden="true">PlusRoundIcon</figcaption>
</figure>
<h2 id="eigenschaften">Eigenschaften</h2>
<table>
<thead>
<tr>
<th>Attribut</th>
<th>Wert</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>iconUrl</code></td>
<td><code>/img/plus_round.png</code></td>
</tr>
<tr>
<td><code>iconSize</code></td>
<td><code>[22, 22]</code></td>
</tr>
<tr>
<td><code>iconAnchor</code></td>
<td><code>[25, 55]</code></td>
</tr>
<tr>
<td><code>className</code></td>
<td><code>absolute top-0 left-0 z-10</code> (Tailwind)</td>
</tr>
</tbody>
</table>
<h2 id="verwendung">Verwendung</h2>
<ul>
<li>Dient als Overlay-Symbol, z.B. für „Gerät hinzufügen“ oder zur
Darstellung über bestehenden Icons</li>
<li>Durch die <code>z-10</code>-Klasse immer im Vordergrund
sichtbar</li>
<li>Kombinierbar mit OverlappingMarkerSpiderfier oder
Marker-Gruppen</li>
</ul>
<h2 id="hinweis">Hinweis</h2>
<ul>
<li>Die Bilddatei <code>/img/plus_round.png</code> muss vorhanden
sein</li>
<li>Kann bei Bedarf dynamisch durch ein anderes Icon ersetzt werden</li>
</ul>
<hr />
<p><a href="../../../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,10 @@
<h1 id="übersicht-docscomponentsiconsdevicesoverlapping">📄 Übersicht:
docs/components/icons/devices/overlapping</h1>
<ul>
<li><a href="PlusRoundIcon.md">PlusRoundIcon</a></li>
</ul>
<figure>
<img src="../../../../screenshots/PlusRoundIcon.png"
alt="PlusRoundIcon" />
<figcaption aria-hidden="true">PlusRoundIcon</figcaption>
</figure>

View File

@@ -0,0 +1,85 @@
<!-- /docs/components/mainComponent/MapComponent.md -->
<h1 id="mapcomponent.js">🗺️ MapComponent.js</h1>
<p>Die zentrale React-Komponente zur Darstellung und Steuerung der
Leaflet-Karte.<br />
Bindet alle Marker, Layer, POIs, Linien und das Kontextmenü dynamisch
ein.</p>
<figure>
<img src="../../screenshots/overview1.png" alt="Overview" />
<figcaption aria-hidden="true">Overview</figcaption>
</figure>
<h2 id="zweck">🎯 Zweck</h2>
<ul>
<li>Initialisiert die Leaflet-Karte (<code>useInitializeMap</code>)</li>
<li>Bindet Marker &amp; Polylinien über Redux und eigene Hooks</li>
<li>Steuerung über Redux-Slices wie <code>selectedArea</code>,
<code>zoomTrigger</code>, <code>polylineVisible</code></li>
<li>Kontextmenüs für Karte, POIs, Polylinien</li>
<li>Unterstützung für Editierfunktionen über <code>editMode</code>
(localStorage)</li>
</ul>
<hr />
<h2 id="hauptbestandteile">🧱 Hauptbestandteile</h2>
<ul>
<li><code>useEffect</code>-Hooks zum Laden und Aktualisieren von:
<ul>
<li>Kartenlayern, POIs, Linien, Rechte, Systeme, Positionen</li>
</ul></li>
<li>Marker-Logik für 15+ Layergruppen (TALAS, ECI, GMA, etc.)</li>
<li>Marker-Overlapping mit <code>OverlappingMarkerSpiderfier</code></li>
<li>Kontextmenüs (Karte &amp; Polylinie)</li>
<li>UI-Komponenten:
<ul>
<li><code>MapLayersControlPanel</code></li>
<li><code>CoordinateInput</code></li>
<li><code>CoordinatePopup</code></li>
<li><code>AddPOIModal</code>, <code>PoiUpdateModal</code>,
<code>VersionInfoModal</code></li>
</ul></li>
</ul>
<hr />
<h2 id="zustand-redux">🧠 Zustand &amp; Redux</h2>
<p>Verwendet umfangreiche Redux-Slices zur Steuerung von:</p>
<ul>
<li><p>Linienstatus, POI-Typen, POI-Icons</p></li>
<li><p>Gerätesysteme &amp; Rechte</p></li>
<li><p>Sichtbarkeit einzelner Layergruppen</p></li>
<li><p>Aktuelle Selektion (Area, Gerät, POI)</p>
<figure>
<img src="../../screenshots/ReaduxSlices.png" alt="ReduxSlices" />
<figcaption aria-hidden="true">ReduxSlices</figcaption>
</figure></li>
</ul>
<hr />
<h2 id="lokale-steuerung">🔧 Lokale Steuerung</h2>
<ul>
<li><p>EditMode wird aus <code>localStorage</code> gelesen</p></li>
<li><p>Karte speichert Zoom &amp; Center dauerhaft im Browser</p></li>
<li><p>Kontextmenü-Einträge ändern sich je nach Rechten &amp; Modus</p>
<figure>
<img src="../../screenshots/LocalStorage.png" alt="LocalStorage" />
<figcaption aria-hidden="true">LocalStorage</figcaption>
</figure></li>
</ul>
<hr />
<h2 id="besonderheiten">🧪 Besonderheiten</h2>
<ul>
<li>Fehlerbehandlung für <code>contextmenu</code>-Fehler eingebaut →
Auto-Neuladen</li>
<li>Alle Marker-Updates mit Overlapping-Check &amp;
Z-Index-Steuerung</li>
<li>Linien enthalten dynamische Tooltips mit
<code>tooltipContents</code></li>
<li>Initiale Datenabfrage über Redux-Thunk-Kaskade</li>
</ul>
<hr />
<h2 id="abhängigkeiten">🔗 Abhängigkeiten</h2>
<ul>
<li>Leaflet, OverlappingMarkerSpiderfier, React-Toastify</li>
<li>Redux Toolkit (Thunks + Selectors)</li>
<li>Tailwind CSS für visuelles Layout</li>
</ul>
<hr />
<p>📄 Pfad: <code>/components/mainComponent/MapComponent.js</code></p>
<hr />
<p><a href="../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,13 @@
<h1 id="übersicht-docscomponentsmaincomponent">📄 Übersicht:
docs/components/mainComponent</h1>
<p>Die zentrale React-Komponente zur Darstellung und Steuerung der
Leaflet-Karte.<br />
Bindet alle Marker, Layer, POIs, Linien und das Kontextmenü dynamisch
ein.</p>
<ul>
<li><a href="MapComponent.md">MapComponent</a></li>
</ul>
<figure>
<img src="../../screenshots/overview1.png" alt="Overview" />
<figcaption aria-hidden="true">Overview</figcaption>
</figure>

View File

@@ -0,0 +1,5 @@
<h1 id="übersicht-docscomponentsmaincomponenthooks">📄 Übersicht:
docs/components/mainComponent/hooks</h1>
<ul>
<li><a href="useInitializeMap.md">useInitializeMap</a></li>
</ul>

View File

@@ -0,0 +1,90 @@
<!-- /docs/components/mainComponent/hooks/useInitializeMap.md -->
<h1 id="useinitializemap.js">🪄 useInitializeMap.js</h1>
<p>Custom React-Hook zur Initialisierung der Leaflet-Karte.<br />
Ermöglicht die einfache Übergabe aller nötigen Parameter und abstrahiert
die <code>initializeMap(...)</code>-Logik.</p>
<hr />
<h2 id="zweck">📦 Zweck</h2>
<ul>
<li>Führt <code>initializeMap(...)</code> nur <strong>einmal</strong>
aus, wenn <code>mapRef</code> existiert und
<code>map === null</code></li>
<li>Kapselt die Initialisierung in ein <code>useEffect</code></li>
</ul>
<hr />
<h2 id="parameter">🔧 Parameter</h2>
<table>
<colgroup>
<col style="width: 26%" />
<col style="width: 21%" />
<col style="width: 51%" />
</colgroup>
<thead>
<tr>
<th>Name</th>
<th>Typ</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>map</code></td>
<td><code>LeafletMap</code> (Zustand)</td>
<td>Wird initialisiert, wenn <code>null</code></td>
</tr>
<tr>
<td><code>mapRef</code></td>
<td><code>ref</code></td>
<td>Referenz auf <code>&lt;div id="map"&gt;</code></td>
</tr>
<tr>
<td><code>setMap</code></td>
<td><code>function</code></td>
<td>Callback zum Setzen der Karteninstanz</td>
</tr>
<tr>
<td><code>setOms</code></td>
<td><code>function</code></td>
<td>Callback für OverlappingMarkerSpiderfier</td>
</tr>
<tr>
<td><code>setMenuItemAdded</code></td>
<td><code>function</code></td>
<td>Wird genutzt, um mehrfaches Menü-Setup zu verhindern</td>
</tr>
<tr>
<td><code>addItemsToMapContextMenu</code></td>
<td><code>function</code></td>
<td>Logik zum Hinzufügen von Kontextmenüeinträgen</td>
</tr>
<tr>
<td><code>hasRights</code></td>
<td><code>boolean</code></td>
<td>Steuerung, ob POI-Menüs angezeigt werden dürfen</td>
</tr>
<tr>
<td><code>setPolylineEventsDisabled</code></td>
<td><code>function</code></td>
<td>Aktiviert/Deaktiviert Polyline-Events global</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="verwendung">🌐 Verwendung</h2>
<p>In <code>MapComponent.js</code>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">useInitializeMap</span>(</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> map<span class="op">,</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> mapRef<span class="op">,</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> setMap<span class="op">,</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> setOms<span class="op">,</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> setMenuItemAdded<span class="op">,</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> addItemsToMapContextMenu<span class="op">,</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> hasRights<span class="op">,</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> value <span class="kw">=&gt;</span> <span class="fu">dispatch</span>(<span class="fu">setDisabled</span>(value))</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>)<span class="op">;</span></span></code></pre></div>
<hr />
<h2 id="quelle">📁 Quelle</h2>
<p>Wrappt <code>initializeMap()</code> aus
<code>/utils/initializeMap.js</code></p>
<hr />
<p><a href="../../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,59 @@
<!-- /docs/components/pois/AddPOIModal.md -->
<h1 id="addpoimodal.js"> AddPOIModal.js</h1>
<p>Zeigt ein modales Formular an, um einen neuen POI auf der Karte zu
erstellen.<br />
Die Koordinaten (<code>latlng</code>) werden automatisch übernommen.</p>
<figure>
<img src="../../screenshots/AddPOIModal.png"
alt="POI hinzufügen Modal" />
<figcaption aria-hidden="true">POI hinzufügen Modal</figcaption>
</figure>
<h2 id="funktionen">Funktionen</h2>
<ul>
<li>POI-Name, Typ und zugehöriges Gerät auswählbar</li>
<li>Koordinatenanzeige (<code>lat</code>, <code>lng</code>)</li>
<li>Dynamisches Laden der Gerätedaten und POI-Typen</li>
<li>Fehleranzeige bei fehlgeschlagenem Speichern</li>
<li>Löst <code>addPoiThunk</code> + Refresh-Trigger
(<code>incrementTrigger</code>) aus</li>
</ul>
<h2 id="props">Props</h2>
<table>
<colgroup>
<col style="width: 14%" />
<col style="width: 15%" />
<col style="width: 70%" />
</colgroup>
<thead>
<tr>
<th>Prop</th>
<th>Typ</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>onClose</code></td>
<td><code>function</code></td>
<td>Schließt das Modal</td>
</tr>
<tr>
<td><code>map</code></td>
<td><code>Leaflet</code></td>
<td>(optional) zum Schließen evtl. offener Popups</td>
</tr>
<tr>
<td><code>latlng</code></td>
<td><code>object</code></td>
<td>Koordinaten für den neuen POI</td>
</tr>
</tbody>
</table>
<h2 id="redux">Redux</h2>
<ul>
<li><code>fetchPoiTypThunk</code>,
<code>fetchPoiIconsDataThunk</code></li>
<li><code>addPoiThunk</code>, <code>resetAddPoiStatus</code></li>
</ul>
<hr />
<p><a href="../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,51 @@
<!-- /docs/components/pois/PoiUpdateModal.md -->
<h1 id="poiupdatemodal.js">✏️ PoiUpdateModal.js</h1>
<p>Ein Dialog zur Aktualisierung oder Löschung bestehender POIs.</p>
<figure>
<img src="../../screenshots/PoiUpdateModal.png"
alt="POI Update Modal" />
<figcaption aria-hidden="true">POI Update Modal</figcaption>
</figure>
<h2 id="features">Features</h2>
<ul>
<li>Zeigt aktuellen Namen, Beschreibung, Gerät und Typ</li>
<li>Gerät und Typ auswählbar via <code>react-select</code></li>
<li>Unterstützt Löschen und Speichern von POIs</li>
<li>Eingebundene Sicherheitsabfrage bei Löschen</li>
</ul>
<h2 id="props">Props</h2>
<table>
<thead>
<tr>
<th>Prop</th>
<th>Typ</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>onClose</code></td>
<td><code>function</code></td>
<td>Schließt das Modal</td>
</tr>
<tr>
<td><code>poiData</code></td>
<td><code>object</code></td>
<td>Bestehende POI-Daten zur Bearbeitung</td>
</tr>
</tbody>
</table>
<h2 id="redux">Redux</h2>
<ul>
<li><code>updatePoiThunk</code>, <code>deletePoiThunk</code></li>
<li><code>fetchLocationDevicesThunk</code>,
<code>fetchPoiTypThunk</code></li>
</ul>
<h2 id="technisches">Technisches</h2>
<ul>
<li>Dynamische Gerätegruppenfilterung basierend auf
<code>mapLayersVisibility</code></li>
<li>Formfelder mit <code>react-select</code> für bessere UX</li>
</ul>
<hr />
<p><a href="../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,10 @@
<h1 id="übersicht-docscomponentspois">📄 Übersicht:
docs/components/pois</h1>
<ul>
<li><a href="AddPOIModal.md">AddPOIModal</a></li>
<li><a href="PoiUpdateModal.md">PoiUpdateModal</a></li>
</ul>
<figure>
<img src="../../screenshots/POIs.png" alt="POIs" />
<figcaption aria-hidden="true">POIs</figcaption>
</figure>

View File

@@ -0,0 +1,154 @@
<!-- /docs/components/uiWidgets/CoordinateInput.md -->
<h1 id="coordinateinput.js">CoordinateInput.js</h1>
<p>Die Komponente <code>CoordinateInput</code> stellt ein einfaches
Eingabefeld für geografische Koordinaten (Latitude, Longitude)
bereit.<br />
Sie dient typischerweise dazu, einen bestimmten Punkt auf der Karte zu
fokussieren bzw. zu markieren.</p>
<hr />
<figure>
<img src="../../screenshots/CoordinateInput.png"
alt="CoordinateInput" />
<figcaption aria-hidden="true">CoordinateInput</figcaption>
</figure>
<hr />
<h2 id="pfad">🔧 Pfad</h2>
<div class="sourceCode" id="cb1"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">/components/uiWidgets/CoordinateInput.js</span></span></code></pre></div>
<hr />
<h2 id="zweck">🎯 Zweck</h2>
<ul>
<li>Eingabe von Koordinaten (z.B. <code>53.2,8.1</code>)</li>
<li>Übergabe dieser Koordinaten an eine Callback-Funktion zur weiteren
Verarbeitung</li>
<li>Positioniert sich dauerhaft in der linken oberen Ecke der Seite
(z.B. zur schnellen Navigation)</li>
</ul>
<hr />
<h2 id="props">⚙️ Props</h2>
<table>
<colgroup>
<col style="width: 18%" />
<col style="width: 8%" />
<col style="width: 73%" />
</colgroup>
<thead>
<tr>
<th>Prop</th>
<th>Typ</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>onCoordinatesSubmit</code></td>
<td><code>function</code></td>
<td>Wird beim Abschicken des Formulars mit dem eingegebenen
Koordinaten-String aufgerufen</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="interne-logik">🧩 Interne Logik</h2>
<div class="sourceCode" id="cb2"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> [coordinates<span class="op">,</span> setCoordinates] <span class="op">=</span> <span class="fu">useState</span>(<span class="st">&quot;&quot;</span>)<span class="op">;</span></span></code></pre></div>
<ul>
<li>Der Eingabewert wird im lokalen State gespeichert</li>
<li>Beim Submit (<code>onSubmit</code>) wird
<code>onCoordinatesSubmit(coordinates)</code> aufgerufen, wenn
gesetzt</li>
</ul>
<hr />
<h2 id="ui-aufbau">🧰 UI-Aufbau</h2>
<ul>
<li>Eingabefeld für Text: Erwartet <code>lat,lng</code></li>
<li>Button: „Zu Marker zoomen“</li>
<li>Position: <code>fixed top-5 left-5</code> → dauerhaft sichtbar</li>
</ul>
<hr />
<h2 id="gestaltung-tailwind-css">🎨 Gestaltung (Tailwind CSS)</h2>
<table>
<colgroup>
<col style="width: 12%" />
<col style="width: 87%" />
</colgroup>
<thead>
<tr>
<th>Element</th>
<th>Klassen</th>
</tr>
</thead>
<tbody>
<tr>
<td>Container</td>
<td><code>fixed top-5 left-5 z-50 bg-white shadow-lg rounded-lg p-4 w-72</code></td>
</tr>
<tr>
<td>Input</td>
<td><code>border p-2 rounded w-full mb-2</code></td>
</tr>
<tr>
<td>Button</td>
<td><code>bg-blue-500 text-white p-2 rounded w-full hover:bg-blue-600</code></td>
</tr>
</tbody>
</table>
<hr />
<h2 id="testfälle">🧪 Testfälle</h2>
<table>
<colgroup>
<col style="width: 31%" />
<col style="width: 68%" />
</colgroup>
<thead>
<tr>
<th>Eingabe</th>
<th>Erwartung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>53.2,8.1</code></td>
<td>Callback <code>onCoordinatesSubmit("53.2,8.1")</code> wird
ausgelöst</td>
</tr>
<tr>
<td>Leer</td>
<td>Callback wird ausgelöst mit leerem String</td>
</tr>
<tr>
<td>Buttonklick</td>
<td>Löst <code>handleSubmit()</code> aus</td>
</tr>
<tr>
<td>Enter-Taste im Eingabefeld</td>
<td>Löst ebenfalls Submit aus</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="erweiterungsideen">💡 Erweiterungsideen</h2>
<ul>
<li>Validierung des Formats (<code>lat,lng</code>) vor dem Absenden</li>
<li>Automatisches Zentrieren der Leaflet-Karte in der
Callback-Funktion</li>
<li>Optionale Markierung des Punkts auf der Karte</li>
</ul>
<hr />
<h2 id="verwendung">📄 Verwendung</h2>
<p>Beispiel in einer Map-Komponente:</p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode jsx"><code class="sourceCode javascriptreact"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">&lt;CoordinateInput</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">onCoordinatesSubmit</span><span class="op">=</span><span class="va">{</span>coords <span class="kw">=&gt;</span> {</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> [lat<span class="op">,</span> lng] <span class="op">=</span> coords<span class="op">.</span><span class="fu">split</span>(<span class="st">&quot;,&quot;</span>)<span class="op">.</span><span class="fu">map</span>(<span class="bu">Number</span>)<span class="op">;</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> map<span class="op">.</span><span class="fu">setView</span>([lat<span class="op">,</span> lng]<span class="op">,</span> <span class="dv">16</span>)<span class="op">;</span> <span class="co">// Leaflet</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> }<span class="va">}</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="fu">/&gt;</span></span></code></pre></div>
<hr />
<h2 id="verwandte-komponenten">📦 Verwandte Komponenten</h2>
<ul>
<li><code>MapComponent.js</code> kann die übergebenen Koordinaten zur
Zentrierung oder Marker-Erstellung nutzen</li>
</ul>
<hr />
<hr />
<p><a href="../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,11 @@
<h1 id="ui-widgets">📄 UI-Widgets</h1>
<ul>
<li><p><a
href="mapLayersControlPanel/mapLayersControlPanel.md">mapLayersControlPanel</a></p></li>
<li><p><a href="CoordinateInput.md">CoordinateInput</a></p></li>
<li><p><a href="VersionInfoModal.md">VersionInfoModal</a></p>
<figure>
<img src="../../screenshots/uiWidgets.png" alt="uiWidgets.png" />
<figcaption aria-hidden="true">uiWidgets.png</figcaption>
</figure></li>
</ul>

View File

@@ -0,0 +1,131 @@
<!-- /docs/components/uiWidgets/VersionInfoModal.md -->
<h1 id="versioninfomodal.js">🪪 VersionInfoModal.js</h1>
<p>Das <code>VersionInfoModal</code> ist ein modales Fenster zur Anzeige
von Unternehmensinformationen und der aktuellen App-Version.<br />
Es wird meist im Footer oder als Info-Schaltfläche in der
Benutzeroberfläche eingeblendet.</p>
<hr />
<figure>
<img src="../../screenshots/VersionInfoModal.png"
alt="VersionInfoModal" />
<figcaption aria-hidden="true">VersionInfoModal</figcaption>
</figure>
<hr />
<h2 id="pfad">🔧 Pfad</h2>
<div class="sourceCode" id="cb1"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">/components/uiWidgets/VersionInfoModal.js</span></span></code></pre></div>
<hr />
<h2 id="zweck">🎯 Zweck</h2>
<p>Die Komponente informiert Nutzer über:</p>
<ul>
<li>Die <strong>aktuelle TALAS.Map Version</strong></li>
<li>Die <strong>Firmenadresse und Kontaktdaten</strong> der Littwin
Systemtechnik GmbH &amp; Co. KG</li>
<li>Eine zentral platzierte Grafik mit dem TALAS-Logo</li>
<li>Eine Schaltfläche zum Schließen des Modals<br />
<img src="../../screenshots/VersionInfoModal2.png"
alt="VersionInfoModal" /></li>
</ul>
<hr />
<h2 id="props">⚙️ Props</h2>
<table>
<colgroup>
<col style="width: 18%" />
<col style="width: 8%" />
<col style="width: 73%" />
</colgroup>
<thead>
<tr>
<th>Prop</th>
<th>Typ</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>showVersionInfoModal</code></td>
<td><code>boolean</code></td>
<td>Steuert, ob das Modal angezeigt wird</td>
</tr>
<tr>
<td><code>closeVersionInfoModal</code></td>
<td><code>function</code></td>
<td>Callback zum Schließen des Modals</td>
</tr>
<tr>
<td><code>APP_VERSION</code></td>
<td><code>string</code></td>
<td>Versionstext (z.B. <code>1.1.188</code>), meist aus
<code>.env.production</code> oder <code>.env.development</code>
geladen</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="verhalten">💡 Verhalten</h2>
<ul>
<li>Wird <code>showVersionInfoModal</code> auf <code>true</code>
gesetzt, erscheint das Modal zentriert über einem halbtransparenten
Overlay</li>
<li>Klick auf den Hintergrund (schwarzes Overlay) oder auf „Schließen“
führt <code>closeVersionInfoModal()</code> aus</li>
</ul>
<hr />
<h2 id="gestaltung">🎨 Gestaltung</h2>
<ul>
<li>Modal-Layout mit Tailwind CSS (<code>fixed</code>,
<code>z-50</code>, <code>bg-white</code>, <code>rounded</code>,
<code>shadow</code>)</li>
<li>Schaltfläche <code>Schließen</code> reagiert auf Hover mit
Farbwechsel (<code>hover:bg-blue-700</code>)</li>
<li>Design folgt der UI-Ästhetik von TALAS.web</li>
</ul>
<hr />
<h2 id="testfälle">🧪 Testfälle</h2>
<table>
<colgroup>
<col style="width: 43%" />
<col style="width: 56%" />
</colgroup>
<thead>
<tr>
<th>Bedingung</th>
<th>Erwartung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>showVersionInfoModal = true</code></td>
<td>Modal wird angezeigt</td>
</tr>
<tr>
<td>Klick auf Hintergrund</td>
<td>Modal wird geschlossen</td>
</tr>
<tr>
<td>Klick auf „Schließen“-Button</td>
<td>Modal wird geschlossen</td>
</tr>
<tr>
<td>Version <code>APP_VERSION = 1.1.290</code></td>
<td>Text „TALAS.Map Version 1.1.290“ sichtbar</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="verknüpfte-dateien">📦 Verknüpfte Dateien</h2>
<ul>
<li><code>.env.production</code> oder <code>.env.development</code>
enthält z.B. <code>NEXT_PUBLIC_APP_VERSION=1.1.188</code></li>
<li>Aufruf in <code>Footer</code> oder <code>Layout</code> zur Anzeige
bei Klick auf „Version“</li>
</ul>
<hr />
<h2 id="verbesserungsideen">🛠 Verbesserungsideen</h2>
<ul>
<li>ESC-Taste als Schließen-Funktion ergänzen</li>
<li>Automatischer Import von Version via
<code>process.env.NEXT_PUBLIC_APP_VERSION</code></li>
</ul>
<hr />
<p><a href="../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,109 @@
<!-- /docs/components/uiWidgets/mapLayersControlPanel/EditModeToggle.md -->
<h1 id="editmodetoggle.js">✏️ EditModeToggle.js</h1>
<p>Die Komponente <code>EditModeToggle</code> stellt einen interaktiven
Umschalter für den Bearbeitungsmodus bereit.<br />
Sie ermöglicht das Ein- und Ausschalten des Modus, in dem POIs,
Polylines (Strecken) und Bereiche bearbeitet werden können.</p>
<hr />
<h2 id="pfad">📦 Pfad</h2>
<div class="sourceCode" id="cb1"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">/components/uiWidgets/mapLayersControlPanel/EditModeToggle.js</span></span></code></pre></div>
<hr />
<h2 id="zweck">🧩 Zweck</h2>
<p>Der Bearbeitungsmodus wirkt sich auf die Interaktivität der Map
aus:</p>
<ul>
<li>Wenn <strong>aktiv</strong>:
<ul>
<li>Checkboxen für Layer sind deaktiviert</li>
<li>POI-Funktionen (Hinzufügen, Verschieben, Löschen) werden
ermöglicht</li>
</ul></li>
<li>Wenn <strong>inaktiv</strong>:
<ul>
<li>Keine Bearbeitung möglich</li>
<li>UI ist auf Betrachtung beschränkt</li>
</ul></li>
</ul>
<hr />
<h2 id="verhalten">🖱 Verhalten</h2>
<p>Beim Klick auf das Icon:</p>
<ol type="1">
<li>Wird der lokale Zustand <code>editMode</code> umgeschaltet</li>
<li><code>localStorage</code> speichert den neuen Status
(<code>true</code> oder <code>false</code>)</li>
<li>Die Seite wird neu geladen (<code>window.location.reload()</code>),
um globale Effekte zu aktivieren</li>
</ol>
<hr />
<h2 id="interner-zustand">🧠 Interner Zustand</h2>
<div class="sourceCode" id="cb2"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> [editMode<span class="op">,</span> setEditMode] <span class="op">=</span> <span class="fu">useState</span>(() <span class="kw">=&gt;</span> localStorage<span class="op">.</span><span class="fu">getItem</span>(<span class="st">&quot;editMode&quot;</span>) <span class="op">===</span> <span class="st">&quot;true&quot;</span>)<span class="op">;</span></span></code></pre></div>
<ul>
<li>Initialisiert aus <code>localStorage</code></li>
<li>Persistente Speicherung des Zustands browserseitig</li>
<li>Aufruf in anderen Komponenten (z.B.
<code>MapLayersControlPanel.js</code>) basiert ebenfalls auf diesem
Wert</li>
</ul>
<hr />
<h2 id="ui-darstellung">🧰 UI-Darstellung</h2>
<ul>
<li>Verwendet <strong>Material-UI-Icons</strong>:
<ul>
<li>🟢 <code>ModeEditIcon</code>: Bearbeitungsmodus <strong>aus</strong>
→ wird angeboten zum <strong>Aktivieren</strong></li>
<li>🔴 <code>EditOffIcon</code>: Bearbeitungsmodus <strong>ein</strong>
→ wird angeboten zum <strong>Deaktivieren</strong></li>
</ul></li>
<li>Tooltip informiert den Nutzer über die jeweilige Aktion</li>
</ul>
<hr />
<h2 id="testfälle">🧪 Testfälle</h2>
<table>
<colgroup>
<col style="width: 25%" />
<col style="width: 75%" />
</colgroup>
<thead>
<tr>
<th>Zustand</th>
<th>Erwartetes Verhalten</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>editMode = false</code></td>
<td>Icon: ✏️ → Tooltip: „Bearbeitungsmodus aktivieren“</td>
</tr>
<tr>
<td><code>editMode = true</code></td>
<td>Icon: 🚫✏️ → Tooltip: „Bearbeitungsmodus deaktivieren“</td>
</tr>
<tr>
<td>Klick auf Icon</td>
<td>Status umschalten, Seite neu laden</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="erweiterungsideen">💡 Erweiterungsideen</h2>
<ul>
<li>🔄 Statt <code>window.location.reload()</code> → globalen Zustand
über Redux-Dispatch steuern</li>
<li>📢 Feedback-Toast nach Umschalten anzeigen (z.B. „Bearbeitungsmodus
aktiviert“)</li>
<li>🧩 Integration in Redux-Store zur globalen Synchronisierung ohne
Reload</li>
</ul>
<hr />
<h2 id="verwandte-komponenten">📄 Verwandte Komponenten</h2>
<ul>
<li><code>MapLayersControlPanel.js</code>: liest
<code>localStorage.editMode</code> und deaktiviert Layer-Checkboxen im
aktiven Modus</li>
<li><code>PoiUpdateModal</code>, <code>AddPOIModal</code>: nutzen den
Bearbeitungsmodus für UI-Freigabe</li>
</ul>
<hr />
<hr />
<p><a href="../../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,213 @@
<!-- /docs/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.md -->
<h1 id="maplayerscontrolpanel.js">🧭 MapLayersControlPanel.js</h1>
<p>Dieses UI-Widget zeigt eine interaktive Steuereinheit für Layer, POIs
und Stationsbereiche auf der rechten Seite der Karte.<br />
Es ist vollständig an den Redux-Store angebunden und reagiert auf
Änderungen der Layer-Sichtbarkeit, Bearbeitungsmodus und
Stationsauswahl.</p>
<hr />
<h2 id="ui-struktur">🧩 UI-Struktur</h2>
<figure>
<img src="../../../screenshots/MapLayersControlPanel.png"
alt="Map layers controll panel" />
<figcaption aria-hidden="true">Map layers controll panel</figcaption>
</figure>
<hr />
<h2 id="pfad">🔧 Pfad</h2>
<div class="sourceCode" id="cb1"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js</span></span></code></pre></div>
<hr />
<h2 id="zweck">📌 Zweck</h2>
<p>Das <code>MapLayersControlPanel</code> ermöglicht Nutzern:</p>
<ul>
<li>Die Auswahl eines Stationsbereichs (Dropdown)</li>
<li>Das Aktivieren/Deaktivieren einzelner GIS-Systeme (Checkboxen)</li>
<li>Das Anzeigen von POIs oder Kabelstrecken (TALAS-spezifisch)</li>
<li>Das Ein-/Ausschalten des Bearbeitungsmodus</li>
<li>Die Steuerung der Karten-Zentrierung über ein Icon</li>
</ul>
<hr />
<h2 id="verwendete-redux-slices">🧠 Verwendete Redux-Slices</h2>
<table>
<colgroup>
<col style="width: 35%" />
<col style="width: 64%" />
</colgroup>
<thead>
<tr>
<th>Slice</th>
<th>Zweck</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>gisStationsStaticDistrictSlice</code></td>
<td>Enthält die Gerätebereiche (mit <code>.Points</code>)</td>
</tr>
<tr>
<td><code>gisSystemStaticSlice</code></td>
<td>Enthält die konfigurierten GIS-Systeme mit Anzeigeerlaubnis</td>
</tr>
<tr>
<td><code>mapLayersSlice</code></td>
<td>Speichert die Sichtbarkeit aller Layer</td>
</tr>
<tr>
<td><code>poiLayerVisibleSlice</code></td>
<td>Steuert Sichtbarkeit der POIs</td>
</tr>
<tr>
<td><code>polylineLayerVisibleSlice</code></td>
<td>Steuert Sichtbarkeit der Kabelstrecken (TALAS)</td>
</tr>
<tr>
<td><code>zoomTriggerSlice</code></td>
<td>Löst Neuzentrierung der Karte aus</td>
</tr>
<tr>
<td><code>selectedAreaSlice</code></td>
<td>Speichert den gewählten Bereich/Station</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="logikübersicht">🔄 Logikübersicht</h2>
<ul>
<li><p><strong>Dropdown Stationsauswahl:</strong><br />
Wird dynamisch aus <code>GisStationsStaticDistrict.Points</code>
befüllt<br />
Nur eindeutige <code>Area_Name</code>, wenn <code>System</code> erlaubt
ist</p></li>
<li><p><strong>Checkboxen für Layer:</strong><br />
Zeigen alle Systeme aus <code>GisSystemStatic</code>, bei denen
<code>Allow === 1</code><br />
Sonderfall: <code>TALAS</code> erhält ein Untermenü für
„Kabelstrecken“</p></li>
<li><p><strong>Lokale Speicherung:</strong><br />
Sichtbarkeiten, Bearbeitungsmodus und POI-Zustand werden in
<code>localStorage</code> geschrieben und bei Initialisierung
geladen</p></li>
<li><p><strong>Bearbeitungsmodus:</strong><br />
Wenn aktiv (<code>editMode === true</code>), sind Layer-Checkboxen
deaktiviert</p></li>
</ul>
<hr />
<h2 id="wichtige-funktionen">📥 Wichtige Funktionen</h2>
<table>
<colgroup>
<col style="width: 44%" />
<col style="width: 55%" />
</colgroup>
<thead>
<tr>
<th>Funktion</th>
<th>Zweck</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>handleAreaChange()</code></td>
<td>Setzt <code>selectedArea</code> im Redux Store</td>
</tr>
<tr>
<td><code>handleCheckboxChange()</code></td>
<td>Schaltet Sichtbarkeit einzelner Layer</td>
</tr>
<tr>
<td><code>handlePolylineCheckboxChange()</code></td>
<td>Aktiviert Sichtbarkeit von Kabelstrecken</td>
</tr>
<tr>
<td><code>handlePoiCheckboxChange()</code></td>
<td>Aktiviert Sichtbarkeit von POIs</td>
</tr>
<tr>
<td><code>handleIconClick()</code></td>
<td>Setzt Station zurück und triggert Zoom</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="debug-hinweise">🐞 Debug-Hinweise</h2>
<ul>
<li><p>Debug-Logs:<br />
<code>__debug.gisStations</code><br />
werden ausgegeben, um sicherzustellen, dass Daten korrekt geladen
wurden</p></li>
<li><p>Warnungen:<br />
Falls <code>.Points</code> nicht vorhanden ist, wird dies in der Konsole
gewarnt</p></li>
</ul>
<hr />
<h2 id="todos-erweiterungsideen">🛠 ToDos / Erweiterungsideen</h2>
<ul>
<li>Checkboxen für Bereiche („Bereiche“, „Standorte“) sind bereits
vorbereitet, aber auskommentiert</li>
<li>Möglichkeit, Tooltips zu aktivieren/deaktivieren?</li>
<li>Gruppierung von Layern nach Typ (z.B. Linien, Geräte, POIs)</li>
</ul>
<hr />
<h2 id="verwendete-komponenten">📄 Verwendete Komponenten</h2>
<ul>
<li><code>MapLayersControlPanel</code></li>
<li><code>EditModeToggle</code> Schaltfläche für Umschalten des
Bearbeitungsmodus</li>
</ul>
<hr />
<h2 id="zustand-lokal-global">✅ Zustand: Lokal &amp; Global</h2>
<ul>
<li><strong>Global:</strong> <code>useSelector(...)</code> aus
Redux</li>
<li><strong>Lokal:</strong> <code>useState(...)</code> für editMode,
stationListing, systemListing</li>
</ul>
<hr />
<h2 id="lokalestorage-keys">📦 LokaleStorage-Keys</h2>
<table>
<thead>
<tr>
<th>Key</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>poiVisible</code></td>
<td>Sichtbarkeit der POI-Marker</td>
</tr>
<tr>
<td><code>polylineVisible</code></td>
<td>Sichtbarkeit der Kabelstrecken</td>
</tr>
<tr>
<td><code>mapLayersVisibility</code></td>
<td>Sichtbarkeiten der einzelnen Systeme</td>
</tr>
<tr>
<td><code>editMode</code></td>
<td>Zustand des Bearbeitungsmodus (true/false)</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="testempfehlung">🧪 Testempfehlung</h2>
<ul>
<li>Dropdown zeigt erwartete <code>Area_Name</code>-Werte?</li>
<li>Layer-Checkboxen werden korrekt gespeichert?</li>
<li>Bei <code>TALAS</code> erscheint zusätzlich: „Kabelstrecken“?</li>
<li>Bei Wechsel der Station wird <code>setSelectedArea</code>
ausgelöst?</li>
</ul>
<hr />
<h2 id="verknüpfte-dateien">🧩 Verknüpfte Dateien</h2>
<ul>
<li><code>redux/slices/webservice/gisStationsStaticDistrictSlice.js</code></li>
<li><code>redux/slices/webservice/gisSystemStaticSlice.js</code></li>
<li><code>redux/slices/mapLayersSlice.js</code></li>
<li><code>redux/slices/selectedAreaSlice.js</code></li>
<li><code>redux/slices/database/polylines/polylineLayerVisibleSlice.js</code></li>
<li><code>redux/slices/database/pois/poiLayerVisibleSlice.js</code></li>
</ul>
<hr />
<hr />
<p><a href="../../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,6 @@
<h1 id="übersicht-docscomponentsuiwidgetsmaplayerscontrolpanel">📄
Übersicht: docs/components/uiWidgets/mapLayersControlPanel</h1>
<ul>
<li><a href="EditModeToggle.md">EditModeToggle</a></li>
<li><a href="MapLayersControlPanel.md">MapLayersControlPanel</a></li>
</ul>

View File

@@ -0,0 +1,107 @@
<!-- /docs/config/README.md -->
<h1 id="konfigurationsübersicht-config">⚙️ Konfigurationsübersicht
(/config)</h1>
<p>Dieses Verzeichnis enthält die zentrale Pfad-Konfigurationsdatei, die
für konsistente URL-Generierung in NodeMap zuständig ist.</p>
<hr />
<h2 id="paths.js">📁 <a href="./paths.md"><code>paths.js</code></a></h2>
<ul>
<li>Berechnet den Basis-Pfad aus <code>.env.production</code> oder
<code>.env.development</code></li>
<li>Liefert <code>BASE_URL</code>, z.B. <code>/talas5</code></li>
<li>Wird in der gesamten App zur dynamischen URL-Erzeugung
verwendet</li>
</ul>
<hr />
<p>Diese Konfiguration stellt sicher, dass NodeMap unabhängig von
Hostname, Port oder Verzeichnisstruktur funktioniert.</p>
<p>📄 Pfad: <code>/config/paths.js</code></p>
<hr />
<h2 id="osmbasierte-open-quellen">OSMbasierte, „open“ Quellen</h2>
<p>Diese sind datenrechtlich offen (ODbL bzw. Community-Lizenzen), aber
das „kostenlos“ gilt nicht im Sinne unbegrenzter TileNutzung. Die
TileServer werden als CommunityRessource bereitgestellt bitte
Policies respektieren.</p>
<ul>
<li>osm-standard (OpenStreetMap)</li>
<li><ul>
<li>Key: Nein</li>
</ul></li>
<li><ul>
<li>Nutzung: FairUse; für produktive/hohe Last eigenen
TileServer/Provider verwenden.</li>
</ul></li>
<li><ul>
<li>Attribution: „© OpenStreetMap contributors“</li>
</ul></li>
<li>osm-humanitarian (HOT)</li>
<li><ul>
<li>Key: Nein</li>
</ul></li>
<li><ul>
<li>Nutzung: FairUse; für größere Last die Betreiber kontaktieren bzw.
andere Infrastruktur nutzen.</li>
</ul></li>
<li><ul>
<li>Attribution: „© OpenStreetMap contributors <br></li>
</ul></li>
<li></li>
<li>Humanitarian OpenStreetMap Team“ cyclosm</li>
<li><ul>
<li>Key: Nein</li>
</ul></li>
<li><ul>
<li>Nutzung: FairUse (bereitgestellt u. a. über OSM France). Für höhere
Last Unterstützung/Hostingoptionen prüfen.</li>
</ul></li>
<li><ul>
<li>Attribution: „CyclOSM“ + „OpenStreetMap contributors“</li>
</ul></li>
<li>PraxisTipps 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 TileProxy 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.</li>
</ul>
<hr />
<p>Kurzantwort: Für kommerzielle Nutzung sind OSMCommunityTileServer
nicht geeignet. Nutze einen bezahlten Anbieter (z.B. Thunderforest,
Tracestrack) oder hoste selbst. Attribution ist immer Pflicht.</p>
<p>Links und Hinweise je Layer/Provider:</p>
<p>OpenStreetMap Standard (osm-standard)</p>
<p>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)</p>
<p>Community-Server (OSM France); keine Produktion/hohe Last Info/Policy
OSM France Tiles: https://tile.openstreetmap.fr/ HOT:
https://www.hotosm.org/ CyclOSM (cyclosm)</p>
<p>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)</p>
<p>Keylos nutzbar mit Attribution; FairUse, für hohe Last über
CARTOPlä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.)</p>
<p>Kommerziell mit APIKey; Pläne von Free bis Pro Pricing:
https://www.thunderforest.com/pricing/ Terms/Attribution:
https://www.thunderforest.com/terms/ Tracestrack Topo</p>
<p>APIKey erforderlich; kostenlose und bezahlte Pläne
Übersicht/Pricing: https://www.tracestrack.com/en/maps/
Nutzungsbedingungen: https://www.tracestrack.com/en/terms/
Empfehlung:</p>
<p>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?</p>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,17 @@
<!-- /docs/config/paths.md -->
<h1 id="paths.js">📁 paths.js</h1>
<p>Berechnet den sauberen <code>BASE_URL</code>-Pfad basierend auf
<code>.env.production</code> oder
<code>public/config.json → basePath</code>.<br />
Entfernt führende und abschließende Slashes.</p>
<h2 id="beispiel">Beispiel</h2>
<p>Wenn <code>basePath = "/talas5/"</code> in config.json gesetzt ist,
wird <code>BASE_URL = "/talas5"</code> verwendet.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> BASE_PATH <span class="op">=</span> basePathRaw<span class="op">.</span><span class="fu">replace</span>(<span class="ss">/</span><span class="sc">^\/|\/$</span><span class="ss">/g</span><span class="op">,</span> <span class="st">&quot;&quot;</span>)<span class="op">;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="im">export</span> <span class="kw">const</span> BASE_URL <span class="op">=</span> BASE_PATH <span class="op">?</span> <span class="vs">`/</span><span class="sc">${</span>BASE_PATH<span class="sc">}</span><span class="vs">`</span> <span class="op">:</span> <span class="st">&quot;&quot;</span><span class="op">;</span></span></code></pre></div>
<h2 id="nutzung">Nutzung</h2>
<ul>
<li>Für konsistente Pfadangaben im gesamten Projekt</li>
</ul>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,10 @@
<!-- /docs/env.local -->
<h3 id="docsenv.local.schema.md">/docs/env.local.schema.md</h3>
<ul>
<li><code>NEXT_PUBLIC_API_HOST</code> → Webservice-DNS oder IP</li>
<li><code>NEXT_PUBLIC_API_BASE_PATH</code> → z.B. <code>talas5</code>,
per Deployment steuerbar</li>
<li><code>DB_NAME</code> → hängt vom Kundenprojekt ab</li>
</ul>
<hr />
<p><a href="README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,14 @@
<h1 id="übersicht-docsguide">📄 Übersicht: docs/guide</h1>
<ul>
<li><a href="dependencies.md">dependencies</a></li>
<li><a href="env.md">env</a></li>
<li><a href="faq.md">faq</a></li>
<li><a href="glossar.md">glossar</a></li>
<li><a href="mock-data.md">mock-data</a></li>
<li><a href="onboarding-checklist.md">onboarding-checklist</a></li>
<li><a href="project-structure.md">project-structure</a></li>
<li><a href="redux-zustand.md">redux-zustand</a></li>
<li><a href="setup-dev.md">setup-dev</a></li>
<li><a href="user-guide.md">user-guide</a></li>
<li><a href="webservices.md">webservices</a></li>
</ul>

View File

@@ -0,0 +1,272 @@
<!-- /docs/guide/dependencies.md-->
<h1 id="abhängigkeiten-in-nodemap-stand-2025">📂 Abhängigkeiten in
NodeMap (Stand: 2025)</h1>
<p>Diese Datei listet alle Drittanbieter-Abhängigkeiten aus der Datei
<code>package.json</code> mit einer kurzen Erklärung, wofür sie im
Projekt verwendet werden.</p>
<hr />
<h2 id="frameworks-tooling">✨ Frameworks &amp; Tooling</h2>
<table>
<colgroup>
<col style="width: 32%" />
<col style="width: 67%" />
</colgroup>
<thead>
<tr>
<th>Paket</th>
<th>Zweck &amp; Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>next</strong></td>
<td>Hauptframework (Next.js) zur Erstellung von React-basierten
SSR/SSG-Apps.</td>
</tr>
<tr>
<td><strong>react</strong> / <strong>react-dom</strong></td>
<td>Grundlage für UI-Komponenten im Projekt.</td>
</tr>
<tr>
<td><strong>tailwindcss</strong> / <strong>postcss</strong> /
<strong>autoprefixer</strong></td>
<td>Styling mit Tailwind. PostCSS verarbeitet CSS, Autoprefixer fügt
vendor-spezifische Präfixe hinzu.</td>
</tr>
<tr>
<td><strong>dotenv</strong></td>
<td>Ermöglicht das Einlesen von <code>.env</code>-Dateien zur Laufzeit
auf dem Server.</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="leaflet-karten">🌐 Leaflet &amp; Karten</h2>
<table>
<colgroup>
<col style="width: 40%" />
<col style="width: 59%" />
</colgroup>
<thead>
<tr>
<th>Paket</th>
<th>Zweck &amp; Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>leaflet</strong></td>
<td>Basiskartenbibliothek zur Darstellung interaktiver Karten.</td>
</tr>
<tr>
<td><strong>leaflet-contextmenu</strong></td>
<td>Kontextmenüs per Rechtsklick in Leaflet.</td>
</tr>
<tr>
<td><strong>leaflet-control-geocoder</strong></td>
<td>Steuerelement für Suche/Geokodierung in Karten.</td>
</tr>
<tr>
<td><strong>leaflet.smooth_marker_bouncing</strong></td>
<td>Animation für Marker-Bounces (visuelles Feedback).</td>
</tr>
<tr>
<td><strong>overlapping-marker-spiderfier-leaflet</strong></td>
<td>Marker-Overlapping-Management mit Spiderfy-Effekt bei Klick.</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="zustand-redux">🪄 Zustand &amp; Redux</h2>
<table>
<colgroup>
<col style="width: 33%" />
<col style="width: 66%" />
</colgroup>
<thead>
<tr>
<th>Paket</th>
<th>Zweck &amp; Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><span class="citation"
data-cites="reduxjs/toolkit">@reduxjs/toolkit</span></strong></td>
<td>Vereinfachte Redux-Nutzung mit Slices &amp; Thunks.</td>
</tr>
<tr>
<td><strong>redux</strong> / <strong>react-redux</strong></td>
<td>Core-State-Management in React-Komponenten.</td>
</tr>
<tr>
<td><strong>redux-thunk</strong></td>
<td>Middleware zur Verarbeitung von asynchronen Aktionen.</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="ui-styling">🦜 UI &amp; Styling</h2>
<table>
<colgroup>
<col style="width: 43%" />
<col style="width: 56%" />
</colgroup>
<thead>
<tr>
<th>Paket</th>
<th>Zweck &amp; Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><span class="citation"
data-cites="mui/icons-material">@mui/icons-material</span></strong></td>
<td>UI-Icons aus der Material UI Sammlung.</td>
</tr>
<tr>
<td><strong><span class="citation"
data-cites="emotion/react">@emotion/react</span></strong>, <strong><span
class="citation"
data-cites="emotion/styled">@emotion/styled</span></strong></td>
<td>Styled Components-Engine, benötigt für MUI Styling.</td>
</tr>
<tr>
<td><strong><span class="citation"
data-cites="heroicons/react">@heroicons/react</span></strong></td>
<td>React-Icons im Hero-Stil für UI.</td>
</tr>
<tr>
<td><strong>react-select</strong></td>
<td>Erweiterte Dropdown-Komponente mit Suchfunktion.</td>
</tr>
<tr>
<td><strong>react-toastify</strong></td>
<td>Benachrichtigungs-Tool für Toast-Meldungen.</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="daten-kommunikation">📁 Daten &amp; Kommunikation</h2>
<table>
<colgroup>
<col style="width: 35%" />
<col style="width: 64%" />
</colgroup>
<thead>
<tr>
<th>Paket</th>
<th>Zweck &amp; Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>axios</strong></td>
<td>HTTP-Client zur API-Kommunikation (z.B. zu Webservices).</td>
</tr>
<tr>
<td><strong>cookies</strong></td>
<td>Zugriff &amp; Verwaltung von Cookies auf Server/Client.</td>
</tr>
<tr>
<td><strong>ws</strong></td>
<td>WebSocket-Kommunikation mit z.B. GMA-Live-Daten.</td>
</tr>
<tr>
<td><strong>xml2js</strong> / <strong>fast-xml-parser</strong></td>
<td>Parsen von XML-Antworten aus Webservices.</td>
</tr>
<tr>
<td><strong>mysql</strong> / <strong>mysql2</strong></td>
<td>Zugriff auf MySQL-Datenbanken.</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="sicherheit-netzwerk">🚫 Sicherheit &amp; Netzwerk</h2>
<table>
<colgroup>
<col style="width: 30%" />
<col style="width: 69%" />
</colgroup>
<thead>
<tr>
<th>Paket</th>
<th>Zweck &amp; Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>http-proxy-middleware</strong></td>
<td>API-Routing &amp; Proxy-Zugriff z.B. für lokale Entwicklung.</td>
</tr>
<tr>
<td><strong>nextjs-cors</strong></td>
<td>CORS-Konfiguration für Next.js API-Routen.</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="entwicklung-test-nur-devdependencies">🎓 Entwicklung &amp; Test
(nur devDependencies)</h2>
<table>
<colgroup>
<col style="width: 60%" />
<col style="width: 39%" />
</colgroup>
<thead>
<tr>
<th>Paket</th>
<th>Zweck &amp; Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>cypress</strong></td>
<td>End-to-End-Testing.</td>
</tr>
<tr>
<td><strong>jest-environment-jsdom</strong> /
<strong>jest-fetch-mock</strong> / <strong>jest-junit</strong></td>
<td>Unit- &amp; Integrationstests.</td>
</tr>
<tr>
<td><strong>identity-obj-proxy</strong></td>
<td>Mocking für CSS-Module im Jest-Testkontext.</td>
</tr>
<tr>
<td><strong>node-fetch</strong>, <strong>node-mocks-http</strong></td>
<td>HTTP-Mocks für Tests.</td>
</tr>
<tr>
<td><strong>husky</strong></td>
<td>Git-Hook-Management (Pre-Commit etc.).</td>
</tr>
<tr>
<td><strong>raw-loader</strong></td>
<td>Import von Rohdaten (z.B. SVG) in Webpack.</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="weitere-tools-hilfen">📄 Weitere Tools &amp; Hilfen</h2>
<table>
<thead>
<tr>
<th>Paket</th>
<th>Zweck &amp; Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>prepare</strong></td>
<td>Wird durch Husky benötigt zum Setup von Hooks.</td>
</tr>
<tr>
<td><strong>bump-version</strong></td>
<td>Interner Versionsbump-Script für <code>appVersion.js</code>.</td>
</tr>
</tbody>
</table>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,108 @@
<!-- /docs/guide/env.md-->
<h1 id="umgebungsvariablen-.env.production-.env.development">🌐
Umgebungsvariablen (<code>.env.production</code> /
<code>.env.development</code>)</h1>
<p>NodeMap verwendet Umgebungsvariablen zur Steuerung von API-Verhalten,
Serverpfaden und Moduswahl (Mock oder Produktion).</p>
<h2 id="speicherort">📂 Speicherort</h2>
<ul>
<li><strong>Entwicklung</strong>: <code>.env.development</code></li>
<li><strong>Produktion</strong>: <code>.env.production</code> (für
<code>npm run build</code> &amp; <code>npm start</code>)</li>
</ul>
<h2 id="wichtige-variablen">🔧 Wichtige Variablen</h2>
<table>
<colgroup>
<col style="width: 18%" />
<col style="width: 12%" />
<col style="width: 69%" />
</colgroup>
<thead>
<tr>
<th>Variable</th>
<th>Beispielwert</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>DB_HOST</code></td>
<td><code>localhost</code></td>
<td>Adresse des Datenbankservers (MySQL)</td>
</tr>
<tr>
<td><code>DB_PORT</code></td>
<td><code>3306</code></td>
<td>Port für die Datenbankverbindung</td>
</tr>
<tr>
<td><code>DB_NAME</code></td>
<td><code>talas</code></td>
<td>Datenbankname</td>
</tr>
<tr>
<td><code>DB_USER</code></td>
<td><code>root</code></td>
<td>Benutzername für MySQL</td>
</tr>
<tr>
<td><code>DB_PASSWORD</code></td>
<td><code>geheim</code></td>
<td>Passwort für MySQL</td>
</tr>
<tr>
<td><code>NEXT_PUBLIC_API_PORT_MODE</code></td>
<td><code>prod</code> oder <code>dev</code></td>
<td>Steuert API-Routing bei Services (z.B. Portwechsel für lokal)</td>
</tr>
<tr>
<td><code>NEXT_PUBLIC_USE_MOCKS</code></td>
<td><code>true</code> oder <code>false</code></td>
<td>Aktiviert den Mockdaten-Modus über <code>/api/mocks/...</code></td>
</tr>
<tr>
<td><code>basePath</code> (in config.json)</td>
<td><code>/talas5</code> oder leer</td>
<td>Optionaler Pfad, falls App unter Subpfad läuft (z.B. IIS). Wird
jetzt in <code>public/config.json</code> gepflegt.</td>
</tr>
<tr>
<td><code>NEXT_PUBLIC_DEBUG</code></td>
<td><code>true</code> oder <code>false</code></td>
<td>Aktiviert zusätzliche <code>console.log</code> Ausgaben für
Debugging im Browser</td>
</tr>
</tbody>
</table>
<h2 id="beispiel-.env.production">📦 Beispiel
<code>.env.production</code></h2>
<pre class="env"><code>DB_HOST=localhost
DB_PORT=3306
DB_NAME=talas
DB_USER=root
DB_PASSWORD=geheim
NEXT_PUBLIC_API_PORT_MODE=prod
NEXT_PUBLIC_USE_MOCKS=false
// public/config.json
{
...
&quot;basePath&quot;: &quot;/talas5&quot;
}
NEXT_PUBLIC_DEBUG=false</code></pre>
<h2 id="beispiel-.env.development">📦 Beispiel
<code>.env.development</code></h2>
<pre class="env"><code>DB_HOST=localhost
DB_PORT=3306
DB_NAME=talas
DB_USER=root
DB_PASSWORD=geheim
NEXT_PUBLIC_API_PORT_MODE=dev
NEXT_PUBLIC_USE_MOCKS=true
// public/config.json
{
...
&quot;basePath&quot;: &quot;/talas5&quot;
}
NEXT_PUBLIC_DEBUG=true</code></pre>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,85 @@
<!-- /docs/guide/faq.md -->
<h1 id="faq-häufige-fragen">❓ FAQ Häufige Fragen</h1>
<hr />
<h3 id="warum-sehe-ich-nur-eine-weiße-seite">🔹 Warum sehe ich nur eine
weiße Seite?</h3>
<ul>
<li>Stelle sicher, dass <code>.env.production</code> korrekt
konfiguriert ist.</li>
<li>Prüfe, ob <code>NEXT_PUBLIC_USE_MOCKS=false</code> gesetzt ist (nur
in Produktion).</li>
<li>Starte den Dienst neu (<strong>NodeMapService</strong>) oder führe
<code>npm start</code> im Terminal aus.</li>
</ul>
<hr />
<h3 id="was-bedeutet-die-url-m12u484">🔹 Was bedeutet die URL
<code>?m=12&amp;u=484</code>?</h3>
<ul>
<li><code>m=12</code> ist die <strong>Map-ID</strong> (z.B.
Leverkusen).</li>
<li><code>u=484</code> ist die <strong>User-ID</strong>.</li>
<li>Diese IDs werden vom übergeordneten System
(<strong>TALAS.web</strong>) übergeben und steuern, was angezeigt
wird.</li>
</ul>
<hr />
<h3 id="wie-kann-ich-pois-hinzufügen-oder-bearbeiten">🔹 Wie kann ich
POIs hinzufügen oder bearbeiten?</h3>
<ul>
<li>Rechtsklick auf die Karte → <strong>„POI hinzufügen“</strong> oder
<strong>„bearbeiten“</strong>.</li>
<li>Daten werden automatisch gespeichert, wenn du das Formular
bestätigst.</li>
</ul>
<hr />
<h3 id="wie-kann-ich-die-karte-lokal-testen-ohne-backend">🔹 Wie kann
ich die Karte lokal testen, ohne Backend?</h3>
<ul>
<li>Setze in <code>.env.development</code> die Variable
<code>NEXT_PUBLIC_USE_MOCKS=true</code>.</li>
<li>Starte mit <code>npm run dev</code>.</li>
<li>Die App lädt jetzt Mockdaten aus <code>/mockData/</code>.</li>
</ul>
<hr />
<h3 id="was-mache-ich-wenn-keine-marker-angezeigt-werden">🔹 Was mache
ich, wenn keine Marker angezeigt werden?</h3>
<ul>
<li>Prüfe die Verbindung zum Webservice:<br />
<code>http://&lt;ip&gt;/talas5/ClientData/WebServiceMap.asmx</code></li>
<li>Stelle sicher, dass die <strong>Map-ID</strong> und
<strong>User-ID</strong> in der URL gültig sind.</li>
</ul>
<hr />
<h3
id="wie-erkenne-ich-ob-mein-layer-z.-b.-talas-wago-gma-geladen-ist">🔹
Wie erkenne ich, ob mein Layer (z.B. TALAS, WAGO, GMA) geladen
ist?</h3>
<ul>
<li>Im rechten Panel (<strong>LayerControl</strong>) sollten Checkboxen
für jeden Layer erscheinen.</li>
<li>Wenn keine Layer sichtbar sind, prüfe
<code>redux/mapLayersSlice</code> und den Webservice
<code>GisSystemStatic</code>.</li>
</ul>
<hr />
<h3 id="was-tun-bei-der-meldung-fehler-beim-laden-der-kartenkacheln">🔹
Was tun bei der Meldung „Fehler beim Laden der Kartenkacheln“?</h3>
<ul>
<li>Verzeichnis <code>C:\inetpub\wwwroot\talas5\TileMap</code>
prüfen.</li>
<li>Kartenkacheln müssen im <code>public/</code>-Pfad korrekt verlinkt
sein (z.B. <code>mapTiles/...</code>).</li>
</ul>
<hr />
<h3 id="wie-kann-ich-die-anwendung-aktualisieren">🔹 Wie kann ich die
Anwendung aktualisieren?</h3>
<ul>
<li><strong>Kleines Update:</strong> Nur <code>.next/</code>
kopieren.</li>
<li><strong>Größeres Update:</strong> Gesamte App inkl.
<code>node_modules</code>, <code>.env.production</code> und
<code>public/</code> ersetzen.</li>
<li>Dienst neu starten.</li>
</ul>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,128 @@
<h1 id="glossar">📘 Glossar</h1>
<p>Eine Übersicht wichtiger Begriffe rund um NodeMap und die verwendeten
Technologien.</p>
<table>
<colgroup>
<col style="width: 18%" />
<col style="width: 81%" />
</colgroup>
<thead>
<tr>
<th>Begriff</th>
<th>Erklärung</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>NodeMap</strong></td>
<td>Die Kartenanwendung zur Darstellung von GIS-Daten (z.B. POIs,
Geräte) in TALAS.web.</td>
</tr>
<tr>
<td><strong>Next.js</strong></td>
<td>Ein Webframework für React, das Server-Rendering und Routing
vereinfacht.</td>
</tr>
<tr>
<td><strong>React</strong></td>
<td>Eine JavaScript-Bibliothek zur Erstellung von Benutzeroberflächen
(UI).</td>
</tr>
<tr>
<td><strong>Redux Toolkit</strong></td>
<td>Ein Tool zur einfacheren Zustandverwaltung (State Management) für
React.</td>
</tr>
<tr>
<td><strong>Tailwind CSS</strong></td>
<td>Ein CSS-Framework mit vordefinierten Klassen für schnelles
UI-Design.</td>
</tr>
<tr>
<td><strong>Leaflet</strong></td>
<td>Eine JavaScript-Bibliothek für interaktive Karten auf
Webseiten.</td>
</tr>
<tr>
<td><strong>POI</strong></td>
<td>„Point of Interest“ Ein Marker auf der Karte (z.B. ein Gerät,
Schacht oder Messpunkt).</td>
</tr>
<tr>
<td><strong>MapComponent</strong></td>
<td>Die Hauptkomponente, die die Karte lädt und alle Inhalte darauf
anzeigt.</td>
</tr>
<tr>
<td><strong>WebService</strong></td>
<td>Ein Serverdienst, der Daten wie POIs, Linien, Geräte liefert (z.B.
aus TALAS).</td>
</tr>
<tr>
<td><strong>.env.production</strong></td>
<td>Eine Konfigurationsdatei mit Zugangsdaten und Einstellungen für den
Live-Betrieb.</td>
</tr>
<tr>
<td><strong>Mockdaten</strong></td>
<td>Testdaten, die lokal geladen werden, wenn kein Server verfügbar ist
(<code>USE_MOCKS=true</code>).</td>
</tr>
<tr>
<td><strong>iFrame</strong></td>
<td>Ein HTML-Element, mit dem eine andere Webseite innerhalb einer Seite
eingebettet wird.</td>
</tr>
<tr>
<td><strong>nssm.exe</strong></td>
<td>Ein Tool, um Node.js-Anwendungen als Windows-Dienst laufen zu
lassen.</td>
</tr>
<tr>
<td><strong>Port 3000</strong></td>
<td>Der lokale Entwicklungs-Port, unter dem NodeMap im Browser
erreichbar ist.</td>
</tr>
<tr>
<td><strong>Redux Slice</strong></td>
<td>Ein Teil des globalen Redux-Zustands, der z.B. POIs oder Linien
speichert.</td>
</tr>
<tr>
<td><strong>Thunk</strong></td>
<td>Eine asynchrone Funktion in Redux, z.B. um Daten vom Server zu
laden.</td>
</tr>
<tr>
<td><strong>Contextmenü</strong></td>
<td>Ein Rechtsklick-Menü mit Funktionen wie „POI hinzufügen“, „Station
öffnen“.</td>
</tr>
<tr>
<td><strong>Layer</strong></td>
<td>Ein Karten-Overlay (z.B. Geräte, Linien), das ein- oder
ausgeblendet werden kann.</td>
</tr>
<tr>
<td><strong>IdSystem / IdMap</strong></td>
<td>Interne IDs zur Zuordnung von Layern und Karten in TALAS.</td>
</tr>
<tr>
<td><strong>GisSystemStatic</strong></td>
<td>Eine Webservice-Antwort mit Systeminformationen für die
Kartendarstellung.</td>
</tr>
<tr>
<td><strong>mapTiles</strong></td>
<td>Bildkacheln (z.B. <code>.png</code>), die die Grundkarte darstellen
wie bei Google Maps.</td>
</tr>
<tr>
<td><strong>TALAS.web</strong></td>
<td>Die bestehende (ältere) Verwaltungssoftware, in die NodeMap
eingebettet wird.</td>
</tr>
</tbody>
</table>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,65 @@
<h1 id="mockdaten-modus-next_public_use_mockstrue">🧪 Mockdaten-Modus
(<code>NEXT_PUBLIC_USE_MOCKS=true</code>)</h1>
<p>Dieses Projekt unterstützt einen optionalen
<strong>Mockdaten-Modus</strong>, um Entwicklung und Tests ohne
Backend/Webservice durchzuführen.</p>
<hr />
<h2 id="zweck-nutzen">🔍 Zweck &amp; Nutzen</h2>
<ul>
<li>Schneller Entwicklungsstart ohne aktive Serververbindung</li>
<li>Stabilere Testszenarien mit festen JSON-Daten</li>
<li>Vollständige Isolation von Backend-Fehlern während der
UI-Entwicklung</li>
</ul>
<hr />
<h2 id="aktivierung">⚙️ Aktivierung</h2>
<p>Mockdaten werden aktiviert durch folgende Umgebungsvariable:</p>
<pre class="env"><code>NEXT_PUBLIC_USE_MOCKS=true</code></pre>
<p>Diese Variable wird in <code>.env.development</code> gesetzt und
<strong>nicht</strong> für die Produktionsumgebung verwendet.<br />
Im Produktivbetrieb steht:</p>
<pre class="env"><code>NEXT_PUBLIC_USE_MOCKS=false</code></pre>
<hr />
<h2 id="funktionsweise">🧩 Funktionsweise</h2>
<p>Wenn <code>NEXT_PUBLIC_USE_MOCKS=true</code> gesetzt ist:</p>
<ul>
<li>Statt realer Webservices werden Endpunkte unter
<code>/pages/api/mocks/webservice/*.js</code> aufgerufen</li>
<li>Diese geben vorbereitete JSON-Dateien unter
<code>/mockData/*.json</code> zurück</li>
</ul>
<hr />
<h2 id="beispiel-aufruf-im-mockmodus">📂 Beispiel-Aufruf im
Mockmodus</h2>
<div class="sourceCode" id="cb3"><pre class="sourceCode ts"><code class="sourceCode typescript"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Beispiel aus fetchGisSystemStaticService.js</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> url <span class="op">=</span> useMocks</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="op">?</span> <span class="st">&quot;/api/mocks/webservice/GisSystemStatic&quot;</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> <span class="op">:</span> <span class="vs">`</span><span class="sc">${</span>apiUrl<span class="sc">}</span><span class="vs">/WebServiceMap.asmx/GisSystemStatic`</span><span class="op">;</span></span></code></pre></div>
<hr />
<h2 id="sicherheit-versionskontrolle">🛡️ Sicherheit &amp;
Versionskontrolle</h2>
<ul>
<li>Alle <code>.json</code>-Dateien im Ordner <code>/mockData/</code>
sind über <code>.gitignore</code> vom Repository ausgeschlossen</li>
<li>So wird verhindert, dass versehentlich sensible Testdaten
veröffentlicht werden</li>
</ul>
<hr />
<h2 id="hinweise">💡 Hinweise</h2>
<ul>
<li>Mockdaten sollen nur die wichtigsten API-Schnittstellen
simulieren</li>
<li>Bei Änderungen am Datenmodell sollten auch die Mockdaten
aktualisiert werden</li>
<li>Eine zentrale Thunk- &amp; Service-Logik entscheidet automatisch, ob
<code>mock</code> oder <code>real</code></li>
</ul>
<hr />
<h2 id="weitere-informationen">🔗 Weitere Informationen</h2>
<ul>
<li><a href="env.md">📄 Umgebungsvariablen</a></li>
<li><a href="webservices.md">📄 Webservices im Detail</a></li>
<li><a href="redux-zustand.md">📄 Zustandverwaltung (Redux)</a></li>
</ul>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,59 @@
<h2 id="onboarding-checkliste-für-neue-entwickler-bei-nodemap">
Onboarding-Checkliste für neue Entwickler bei NodeMap</h2>
<p>Willkommen im NodeMap-Team! Diese Checkliste begleitet dich Schritt
für Schritt beim Einstieg ins Projekt.</p>
<hr />
<h3 id="schritte-zum-start">🚦 Schritte zum Start</h3>
<ol type="1">
<li><p><strong>README.md lesen</strong><br />
<em>Verschaffe dir einen Überblick über das Projekt.</em><br />
☐ Erledigt</p></li>
<li><p><strong>Repository clonen &amp; installieren</strong></p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">git</span> clone http://10.10.0.12:3000/ISA/nodeMap</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="bu">cd</span> nodeMap</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="ex">npm</span> install</span></code></pre></div>
<p>☐ Erledigt</p></li>
<li><p><strong><code>.env.development</code> anlegen</strong><br />
<em>Siehe <a href="docs/guide/env.md">env.md</a> für Details.</em><br />
☐ Erledigt</p></li>
<li><p><strong>Mock-Modus aktivieren</strong></p>
<pre class="env"><code>NEXT_PUBLIC_USE_MOCKS=true</code></pre>
<p>☐ Erledigt</p></li>
<li><p><strong>Projekt starten</strong></p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ex">npm</span> run dev</span></code></pre></div>
<p>☐ Erledigt</p></li>
<li><p><strong>App im Browser öffnen</strong><br />
<em>Gehe zu:</em> <a
href="http://localhost:3000">http://localhost:3000</a><br />
☐ Erledigt</p></li>
<li><p><strong>POIs testen</strong><br />
<em>Hinzufügen, Verschieben, Löschen siehe <a
href="docs/guide/user-guide.md">user-guide.md</a>.</em><br />
☐ Erledigt</p></li>
<li><p><strong>Redux DevTools installieren &amp; testen</strong><br />
<em>Empfohlen für Debugging.</em><br />
☐ Erledigt</p></li>
<li><p><strong>Projektstruktur ansehen</strong><br />
<em>Wichtige Ordner: <code>components/</code>, <code>redux/</code>,
<code>services/</code> siehe <a
href="docs/guide/project-structure.md">project-structure.md</a>.</em><br />
☐ Erledigt</p></li>
<li><p><strong>Webservices überfliegen</strong><br />
<em>Siehe <a
href="docs/guide/webservices.md">webservices.md</a>.</em><br />
☐ Erledigt</p></li>
<li><p><strong>Fehlerbehandlung beachten</strong><br />
<em>Hinweise dazu findest du im README.</em><br />
☐ Erledigt</p></li>
<li><p><strong>Fragen notieren &amp; klären</strong><br />
<em>Sammle offene Punkte und sprich sie im Team an.</em><br />
☐ Erledigt</p></li>
</ol>
<hr />
<p><strong>Tipp:</strong> Hake jeden Schritt ab, sobald du ihn erledigt
hast.<br />
Viel Erfolg beim Einstieg! 🎉</p>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,135 @@
<h2 id="projektstruktur">🧱 Projektstruktur</h2>
<div class="sourceCode" id="cb1"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">components/</span> → UI-Komponenten inkl. Karte und Layer-Control-Panel <span class="er">(</span><span class="kw">`</span><span class="ex">MapLayersControlPanel</span><span class="kw">`)</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="ex">📦components</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> 📂contextmenu</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜CoordinatePopup.js</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┗ 📜useMapContextMenu.js</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> 📂gisPolylines</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📂icons</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜CircleIcon.js</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜EndIcon.js</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜StartIcon.js</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┗ 📜SupportPointIcons.js</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┗ 📜PolylineContextMenu.js</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> 📂icons</span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┗ 📂devices</span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┗ 📂overlapping</span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┗ 📜PlusRoundIcon.js</span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> 📂mainComponent</span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📂hooks</span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┗ 📜useInitializeMap.js</span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┗ 📜MapComponent.js</span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> 📂pois</span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜AddPOIModal.js</span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┗ 📜PoiUpdateModal.js</span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> 📂uiWidgets</span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📂mapLayersControlPanel</span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜EditModeToggle.js</span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┗ 📜MapLayersControlPanel.js</span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜CoordinateInput.js</span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┗ 📜VersionInfoModal.js</span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> 📜TestScript.js</span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a><span class="ex">config/</span> → zentrale Variablen <span class="er">(</span><span class="ex">.env.development,</span> .env.production<span class="kw">)</span></span>
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-35"><a href="#cb1-35" aria-hidden="true" tabindex="-1"></a><span class="ex">hooks/</span> → eigene React-Hooks</span>
<span id="cb1-36"><a href="#cb1-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-37"><a href="#cb1-37" aria-hidden="true" tabindex="-1"></a><span class="ex">utils/</span> → POI- und Linienverarbeitung</span>
<span id="cb1-38"><a href="#cb1-38" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-39"><a href="#cb1-39" aria-hidden="true" tabindex="-1"></a><span class="ex">lib/</span> → Formatierungen, Umrechnungen</span>
<span id="cb1-40"><a href="#cb1-40" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-41"><a href="#cb1-41" aria-hidden="true" tabindex="-1"></a><span class="ex">public/</span> → Bilder, Icons, mapTiles sind nicht im nodeMap Projekt Verzeichnis sondern in TALAS Verzeichnis</span>
<span id="cb1-42"><a href="#cb1-42" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-43"><a href="#cb1-43" aria-hidden="true" tabindex="-1"></a><span class="ex">pages/</span> → Next.js Seiten <span class="kw">&amp;</span> <span class="ex">Routen</span></span>
<span id="cb1-44"><a href="#cb1-44" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-45"><a href="#cb1-45" aria-hidden="true" tabindex="-1"></a><span class="ex">scripts/</span> → lokale Tools <span class="er">(</span><span class="ex">nur</span> Dev<span class="kw">)</span></span>
<span id="cb1-46"><a href="#cb1-46" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-47"><a href="#cb1-47" aria-hidden="true" tabindex="-1"></a><span class="ex">redux/</span> → globale Zustände <span class="er">(</span><span class="ex">Slices</span><span class="kw">)</span></span>
<span id="cb1-48"><a href="#cb1-48" aria-hidden="true" tabindex="-1"></a><span class="ex">📦redux</span></span>
<span id="cb1-49"><a href="#cb1-49" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> 📂slices</span>
<span id="cb1-50"><a href="#cb1-50" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📂database</span>
<span id="cb1-51"><a href="#cb1-51" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📂pois</span>
<span id="cb1-52"><a href="#cb1-52" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜addPoiOnPolylineSlice.js</span>
<span id="cb1-53"><a href="#cb1-53" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜addPoiSlice.js</span>
<span id="cb1-54"><a href="#cb1-54" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜currentPoiSlice.js</span>
<span id="cb1-55"><a href="#cb1-55" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜poiIconsDataSlice.js</span>
<span id="cb1-56"><a href="#cb1-56" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜poiLayerVisibleSlice.js</span>
<span id="cb1-57"><a href="#cb1-57" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜poiReadFromDbTriggerSlice.js</span>
<span id="cb1-58"><a href="#cb1-58" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜poiTypesSlice.js</span>
<span id="cb1-59"><a href="#cb1-59" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜poiTypSlice.js</span>
<span id="cb1-60"><a href="#cb1-60" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜readPoiMarkersStoreSlice.js</span>
<span id="cb1-61"><a href="#cb1-61" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┗ 📜selectedPoiSlice.js</span>
<span id="cb1-62"><a href="#cb1-62" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📂polylines</span>
<span id="cb1-63"><a href="#cb1-63" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜gisLinesSlice.js</span>
<span id="cb1-64"><a href="#cb1-64" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜polylineContextMenuSlice.js</span>
<span id="cb1-65"><a href="#cb1-65" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜polylineEventsDisabledSlice.js</span>
<span id="cb1-66"><a href="#cb1-66" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┗ 📜polylineLayerVisibleSlice.js</span>
<span id="cb1-67"><a href="#cb1-67" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜locationDevicesFromDBSlice.js</span>
<span id="cb1-68"><a href="#cb1-68" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜locationDevicesSlice.js</span>
<span id="cb1-69"><a href="#cb1-69" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┗ 📜priorityConfigSlice.js</span>
<span id="cb1-70"><a href="#cb1-70" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📂webservice</span>
<span id="cb1-71"><a href="#cb1-71" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜gisLinesStatusSlice.js</span>
<span id="cb1-72"><a href="#cb1-72" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜gisStationsMeasurementsSlice.js</span>
<span id="cb1-73"><a href="#cb1-73" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜gisStationsStaticDistrictSlice.js</span>
<span id="cb1-74"><a href="#cb1-74" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜gisStationsStatusDistrictSlice.js</span>
<span id="cb1-75"><a href="#cb1-75" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜gisSystemStaticSlice.js</span>
<span id="cb1-76"><a href="#cb1-76" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┗ 📜userRightsSlice.js</span>
<span id="cb1-77"><a href="#cb1-77" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜lineVisibilitySlice.js</span>
<span id="cb1-78"><a href="#cb1-78" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜mapLayersSlice.js</span>
<span id="cb1-79"><a href="#cb1-79" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜selectedAreaSlice.js</span>
<span id="cb1-80"><a href="#cb1-80" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜selectedDeviceSlice.js</span>
<span id="cb1-81"><a href="#cb1-81" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜urlParameterSlice.js</span>
<span id="cb1-82"><a href="#cb1-82" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┗ 📜zoomTriggerSlice.js</span>
<span id="cb1-83"><a href="#cb1-83" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> 📂thunks</span>
<span id="cb1-84"><a href="#cb1-84" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📂database</span>
<span id="cb1-85"><a href="#cb1-85" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📂pois</span>
<span id="cb1-86"><a href="#cb1-86" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜addPoiThunk.js</span>
<span id="cb1-87"><a href="#cb1-87" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜deletePoiThunk.js</span>
<span id="cb1-88"><a href="#cb1-88" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜fetchPoiIconsDataThunk.js</span>
<span id="cb1-89"><a href="#cb1-89" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┣ 📜fetchPoiTypThunk.js</span>
<span id="cb1-90"><a href="#cb1-90" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┗ 📜updatePoiThunk.js</span>
<span id="cb1-91"><a href="#cb1-91" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📂polylines</span>
<span id="cb1-92"><a href="#cb1-92" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┃ ┗ 📜fetchGisLinesThunk.js</span>
<span id="cb1-93"><a href="#cb1-93" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜fetchLocationDevicesThunk.js</span>
<span id="cb1-94"><a href="#cb1-94" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜fetchPriorityConfigThunk.js</span>
<span id="cb1-95"><a href="#cb1-95" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┗ 📜getDeviceIdByNameThunk.js</span>
<span id="cb1-96"><a href="#cb1-96" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┗ 📂webservice</span>
<span id="cb1-97"><a href="#cb1-97" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜fetchGisLinesStatusThunk.js</span>
<span id="cb1-98"><a href="#cb1-98" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜fetchGisStationsMeasurementsThunk.js</span>
<span id="cb1-99"><a href="#cb1-99" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜fetchGisStationsStaticDistrictThunk.js</span>
<span id="cb1-100"><a href="#cb1-100" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜fetchGisStationsStatusDistrictThunk.js</span>
<span id="cb1-101"><a href="#cb1-101" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜fetchGisSystemStaticThunk.js</span>
<span id="cb1-102"><a href="#cb1-102" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┗ 📜fetchUserRightsThunk.js</span>
<span id="cb1-103"><a href="#cb1-103" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> 📜store.js</span>
<span id="cb1-104"><a href="#cb1-104" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-105"><a href="#cb1-105" aria-hidden="true" tabindex="-1"></a><span class="ex">services/</span> → API-Kommunikation, Mock-Logik</span>
<span id="cb1-106"><a href="#cb1-106" aria-hidden="true" tabindex="-1"></a><span class="ex">📦services</span></span>
<span id="cb1-107"><a href="#cb1-107" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> 📂database</span>
<span id="cb1-108"><a href="#cb1-108" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📂pois</span>
<span id="cb1-109"><a href="#cb1-109" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜addPoiService.js</span>
<span id="cb1-110"><a href="#cb1-110" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜deletePoiService.js</span>
<span id="cb1-111"><a href="#cb1-111" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜fetchPoiDataByIdService.js</span>
<span id="cb1-112"><a href="#cb1-112" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜fetchPoiDataService.js</span>
<span id="cb1-113"><a href="#cb1-113" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜fetchPoiIconsDataService.js</span>
<span id="cb1-114"><a href="#cb1-114" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┣ 📜fetchPoiTypService.js</span>
<span id="cb1-115"><a href="#cb1-115" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┗ 📜updatePoiService.js</span>
<span id="cb1-116"><a href="#cb1-116" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📂polylines</span>
<span id="cb1-117"><a href="#cb1-117" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┃ ┗ 📜fetchGisLinesService.js</span>
<span id="cb1-118"><a href="#cb1-118" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜fetchDeviceNameByIdService.js</span>
<span id="cb1-119"><a href="#cb1-119" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜fetchLocationDevicesService.js</span>
<span id="cb1-120"><a href="#cb1-120" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜fetchPriorityConfigService.js</span>
<span id="cb1-121"><a href="#cb1-121" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜getDeviceIdByNameService.js</span>
<span id="cb1-122"><a href="#cb1-122" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┗ 📜updateLocationInDatabaseService.js</span>
<span id="cb1-123"><a href="#cb1-123" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> 📂utils</span>
<span id="cb1-124"><a href="#cb1-124" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┗ 📜fetchWithTimeout.js</span>
<span id="cb1-125"><a href="#cb1-125" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> 📂webservice</span>
<span id="cb1-126"><a href="#cb1-126" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜fetchGisLinesStatusService.js</span>
<span id="cb1-127"><a href="#cb1-127" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜fetchGisStationsMeasurementsService.js</span>
<span id="cb1-128"><a href="#cb1-128" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜fetchGisStationsStaticDistrictService.js</span>
<span id="cb1-129"><a href="#cb1-129" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜fetchGisStationsStatusDistrictService.js</span>
<span id="cb1-130"><a href="#cb1-130" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┣ 📜fetchGisSystemStaticService.js</span>
<span id="cb1-131"><a href="#cb1-131" aria-hidden="true" tabindex="-1"></a> <span class="ex"></span> ┗ 📜fetchUserRightsService.js</span></code></pre></div>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,23 @@
<!-- /docs/guide/redux-zustand.md-->
<h2 id="zustand-redux">🧠 Zustand: Redux</h2>
<p>Die Anwendung verwendet vollständig <strong>Redux Toolkit</strong>
für die globale Zustandverwaltung.</p>
<ul>
<li>Dynamische Gerätegruppen (Layer) werden automatisch über
<code>IdSystem</code> aus <code>GisSystemStatic</code>
initialisiert</li>
<li>Layer-Steuerung erfolgt über <code>system-&lt;IdSystem&gt;</code>
Keys im Redux <code>mapLayersSlice</code></li>
<li>Marker für Geräte werden über Vergleich <code>System</code> ↔︎
<code>IdSystem</code> angezeigt</li>
</ul>
<h3 id="gründe-für-redux">Gründe für Redux :</h3>
<ul>
<li>Bessere Nachvollziehbarkeit durch zentrale Store-Struktur</li>
<li>Unterstützung für DevTools, Logging, Debugging</li>
<li>Einheitliche Behandlung von Status, auch bei komplexen
Komponenten</li>
</ul>
<p>➡ Neue Features bitte ausschließlich mit Redux umsetzen!</p>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,46 @@
<h1 id="lokale-entwicklung-mit-nodemap">🧑‍💻 Lokale Entwicklung mit
NodeMap</h1>
<p>Diese Anleitung richtet sich an Entwickler, die NodeMap lokal
weiterentwickeln möchten.</p>
<hr />
<h2 id="voraussetzungen">Voraussetzungen</h2>
<ul>
<li>Node.js v18+</li>
<li>NPM</li>
<li>Chrome / Edge / Firefox</li>
</ul>
<hr />
<h2 id="schritte">Schritte</h2>
<div class="sourceCode" id="cb1"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">npm</span> install</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ex">npm</span> run dev</span></code></pre></div>
<hr />
<h2 id="optionen">Optionen</h2>
<ul>
<li><strong>Mockdaten-Modus aktivieren:</strong></li>
</ul>
<pre class="env"><code>NEXT_PUBLIC_USE_MOCKS=true</code></pre>
<ul>
<li><strong>Umgebungsvariablen lokal definieren:</strong></li>
</ul>
<p>Datei <code>.env.development</code> mit Inhalten wie:</p>
<pre><code>NEXT_PUBLIC_API_URL=http://localhost:3000
NEXT_PUBLIC_USE_MOCKS=true</code></pre>
<hr />
<h2 id="debugging">Debugging</h2>
<ul>
<li>Verwende <code>console.log</code> in Komponenten oder
Redux-Slices</li>
<li>Browser-DevTools &amp; Redux DevTools empfohlen</li>
</ul>
<hr />
<h2 id="weitere-dokumentation">Weitere Dokumentation</h2>
<ul>
<li>Projektstruktur: <a
href="project-structure.md">project-structure.md</a></li>
<li>Webservices: <a href="webservices.md">webservices.md</a></li>
<li>Zustandverwaltung: <a
href="redux-zustand.md">redux-zustand.md</a></li>
</ul>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,22 @@
<!-- /docs/quide/user-guide.md -->
<h2 id="benutzeranleitung">🧭 Benutzeranleitung</h2>
<ul>
<li><strong>Station öffnen:</strong> Rechte Maustaste → “Station öffnen”
oder “Station öffnen (Tab)”</li>
<li><strong>POI hinzufügen:</strong> Rechtsklick → “POI hinzufügen” →
Formular ausfüllen</li>
<li><strong>POI bearbeiten/löschen:</strong> Kontextmenü verwenden</li>
<li><strong>POI verschieben:</strong> Drag &amp; Drop des Markers,
automatische DB-Aktualisierung</li>
<li><strong>Koordinaten anzeigen:</strong> Kontextmenüoption nutzen</li>
<li><strong>Zoom:</strong> Mausrad oder Kontextmenüoption</li>
<li><strong>Layer steuern:</strong> GIS-Geräte-Layer (z.B. TALAS, WAGO,
GMA) ein-/ausblenden über Checkboxen im rechten Panel
(<code>MapLayersControlPanel</code>)</li>
<li><strong>Station auswählen:</strong> Dropdown oben rechts</li>
<li><strong>Zentrieren:</strong> Rechtsklick → “Hier zentrieren”</li>
<li><strong>Geräte-Kontextmenü:</strong> Rechtsklick auf Marker →
„Station öffnen (Tab)“</li>
</ul>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,67 @@
<h2 id="webservice-anbindung-backend-talas.web">📡 Webservice-Anbindung
(Backend: TALAS.web)</h2>
<p>NodeMap verwendet verschiedene Webservices, die von <strong>TALAS
V5/TALAS.web</strong> im IIS bereitgestellt werden.<br />
Diese Services liefern dynamische GIS-, Geräte- und Statusdaten für die
MapComponent.</p>
<h3 id="url-des-webservice">URL des Webservice:</h3>
<pre><code>http://localhost/talas5/ClientData/WebServiceMap.asmx</code></pre>
<blockquote>
<p>🔧 In <code>.env.production</code> oder <code>config.js</code> muss
die Adresse je nach Umgebung angepasst werden (z.B.
<code>http://10.10.0.13/talas5/...</code>)</p>
</blockquote>
<h3 id="verfügbare-methoden-auszug">Verfügbare Methoden (Auszug):</h3>
<table>
<colgroup>
<col style="width: 39%" />
<col style="width: 60%" />
</colgroup>
<thead>
<tr>
<th>Endpunkt</th>
<th>Zweck / Datenquelle</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>CablesStatic</code></td>
<td>Liste aller Stränge</td>
</tr>
<tr>
<td><code>GetIconsStatic</code></td>
<td>Liste aller Icons</td>
</tr>
<tr>
<td><code>GisLinesStatus</code></td>
<td>Liste aller Status der Linien</td>
</tr>
<tr>
<td><code>GisStationsMeasurements</code></td>
<td>Liste aller Messungen der Geräte</td>
</tr>
<tr>
<td><code>GisStationsStaticDistrict</code></td>
<td>Liste aller Geräte einer bestimmten Karte</td>
</tr>
<tr>
<td><code>GisStationsStatusDistrict</code></td>
<td>Liste aller Statis der Geräte</td>
</tr>
<tr>
<td><code>GisSystemStatic</code></td>
<td>Liste aller angezeigten Systeme</td>
</tr>
</tbody>
</table>
<p>Die Webservices liefern JSON und werden im Frontend über
<code>services/*.js</code> abgefragt.<br />
Die Daten werden verarbeitet, zwischengespeichert und z.T. über Redux
in der Karte dargestellt.</p>
<p>➡ Damit alles funktioniert, müssen:</p>
<ul>
<li>der IIS laufen</li>
<li>der <code>WebServiceMap.asmx</code> erreichbar sein</li>
</ul>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,11 @@
<h1 id="übersicht-docshooks">📄 Übersicht: docs/hooks</h1>
<ul>
<li><a href="useCreateAndSetDevices.md">useCreateAndSetDevices</a></li>
<li><a href="useDynamicMarkerLayers.md">useDynamicMarkerLayers</a></li>
<li><a href="useLayerVisibility.md">useLayerVisibility</a></li>
<li><a href="useLineData.md">useLineData</a></li>
<li><a href="useMapComponentState.md">useMapComponentState</a></li>
<li><a href="useMarkerLayers.md">useMarkerLayers</a></li>
<li><a
href="usePolylineTooltipLayer.md">usePolylineTooltipLayer</a></li>
</ul>

View File

@@ -0,0 +1,28 @@
<h1 id="übersicht-docshookslayers">📄 Übersicht: docs/hooks/layers</h1>
<ul>
<li><a href="useAreaMarkersLayer.md">useAreaMarkersLayer</a></li>
<li><a
href="useCiscoRouterMarkersLayer.md">useCiscoRouterMarkersLayer</a></li>
<li><a href="useDauzMarkersLayer.md">useDauzMarkersLayer</a></li>
<li><a href="useDrawLines.md">useDrawLines</a></li>
<li><a href="useEciMarkersLayer.md">useEciMarkersLayer</a></li>
<li><a href="useGmaMarkersLayer.md">useGmaMarkersLayer</a></li>
<li><a
href="useLteModemMarkersLayer.md">useLteModemMarkersLayer</a></li>
<li><a
href="useMessstellenMarkersLayer.md">useMessstellenMarkersLayer</a></li>
<li><a href="useOtdrMarkersLayer.md">useOtdrMarkersLayer</a></li>
<li><a href="useSiemensMarkersLayer.md">useSiemensMarkersLayer</a></li>
<li><a
href="useSmsfunkmodemMarkersLayer.md">useSmsfunkmodemMarkersLayer</a></li>
<li><a
href="useSonstigeMarkersLayer.md">useSonstigeMarkersLayer</a></li>
<li><a href="useTalasMarkersLayer.md">useTalasMarkersLayer</a></li>
<li><a
href="useTalasiclMarkersLayer.md">useTalasiclMarkersLayer</a></li>
<li><a
href="useTkComponentsMarkersLayer.md">useTkComponentsMarkersLayer</a></li>
<li><a href="useUlafMarkersLayer.md">useUlafMarkersLayer</a></li>
<li><a href="useWagoMarkersLayer.md">useWagoMarkersLayer</a></li>
<li><a href="useWdmMarkersLayer.md">useWdmMarkersLayer</a></li>
</ul>

View File

@@ -0,0 +1,16 @@
<!-- /docs/hooks/layers/useAreaMarkersLayer.md -->
<h1 id="useareamarkerslayer.js">🗺️ useAreaMarkersLayer.js</h1>
<p>Lädt Bereichs-/Stationsmarker aus einer API und rendert sie auf der
Karte.</p>
<h2 id="features">Features</h2>
<ul>
<li>Marker mit Tooltip für Standort &amp; Bereich</li>
<li>Draggable Marker (verschiebbar)</li>
<li>Automatischer API-Fetch mit <code>fetch(...)</code></li>
<li>Dynamisches Layer-Handling via localStorage
(“mapLayersVisibility”)</li>
<li>Automatisches Speichern neuer Koordinaten per
<code>updateAreaThunk()</code></li>
</ul>
<hr />
<p><a href="../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,13 @@
<!-- /docs/hooks/layers/useDrawLines.md -->
<h1 id="usedrawlines.js">🧬 useDrawLines.js</h1>
<p>Hook zur Konvertierung von GIS-Linien in kartentaugliche
Koordinatenpaare.</p>
<h2 id="schritte">Schritte</h2>
<ul>
<li>Lädt Linien mit <code>fetchGisLinesThunk()</code></li>
<li>Wandelt <code>points[x, y]</code> in Leaflet-Koordinaten
<code>[lat, lng]</code> um</li>
<li>Gibt <code>setLinePositions([...])</code> zurück</li>
</ul>
<hr />
<p><a href="../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,24 @@
<!-- /docs/hooks/useCreateAndSetDevices.md -->
<h1 id="usecreateandsetdevices.js">🛠️ useCreateAndSetDevices.js</h1>
<p>Custom Hook zur Initialisierung von Leaflet-Markern für ein
bestimmtes System.<br />
Bindet <code>createAndSetDevices(...)</code> automatisch in einen
<code>useEffect</code>.</p>
<p>Beispiel: TALAS Layer ist mit Pfeilen markiert<br />
<img src="../screenshots/TALAS-Layer.png" alt="TALAS-Layer" /></p>
<h2 id="parameter">Parameter</h2>
<ul>
<li><code>systemId</code>: ID des Gerätesystems (z.B. 1 = TALAS)</li>
<li><code>setMarkersFunction</code>: Funktion zum Speichern der
erzeugten Marker</li>
<li><code>GisSystemStatic</code>: Systemdaten aus Redux</li>
<li><code>priorityConfig</code>: Konfigurationsobjekt zur
Prioritätsbewertung</li>
</ul>
<h2 id="redux">Redux</h2>
<ul>
<li>Bezieht <code>polylineEventsDisabled</code> aus Redux zur Steuerung
der Interaktivität</li>
</ul>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,22 @@
<!-- /docs/hooks/useDynamicMarkerLayers.md -->
<h1 id="usedynamicmarkerlayers.js">🔄 useDynamicMarkerLayers.js</h1>
<p>Verwaltet alle Marker-Layergruppen dynamisch und modular in einem
zentralen Hook.</p>
<p>Beispiel: TALAS Layer ist mit Pfeilen markiert<br />
<img src="../screenshots/TALAS-Layer.png" alt="TALAS-Layer" /></p>
<h2 id="funktionen">Funktionen</h2>
<ul>
<li>Initialisiert LayerGroups für 15+ Gerätesysteme</li>
<li>Ruft <code>createAndSetDevices()</code> pro System-ID auf</li>
<li>Führt automatisch Overlap-Check aus
(<code>checkOverlappingMarkers</code>)</li>
<li>Speichert erzeugte Marker in <code>setMarkerStates</code></li>
</ul>
<h2 id="voraussetzungen">Voraussetzungen</h2>
<ul>
<li>Karte (<code>map</code>) muss bereit sein</li>
<li><code>GisSystemStatic</code> + <code>priorityConfig</code> +
Marker-Setter müssen übergeben werden</li>
</ul>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,25 @@
<!-- /docs/hooks/useLayerVisibility.md -->
<h1 id="uselayervisibility.js">👁️ useLayerVisibility.js</h1>
<p>Custom Hook zur dynamischen Steuerung von Layer-Sichtbarkeit
basierend auf Redux.</p>
<p>Beispiel: TALAS Layer ist mit Pfeilen markiert<br />
<img src="../screenshots/mapLayersVisibilityTALAS.png"
alt="TALAS-Layer" /><br />
Redux<br />
<img src="../screenshots/mapLayersVisibilityRedux.png"
alt="TALAS-Layer" /><br />
Local Storage<br />
<img src="../screenshots/mapLayersVisibility.png"
alt="TALAS-Layer" /></p>
<h2 id="features">Features</h2>
<ul>
<li>Entfernt oder zeigt Marker je nach
<code>mapLayersVisibility</code></li>
<li>Nutzt <code>OverlappingMarkerSpiderfier</code></li>
<li>Nutzt Layer-IDs</li>
</ul>
<h2 id="intern">Intern</h2>
<p>Verwendet <code>addContextMenuToMarker()</code> zur
Kontextmenüintegration pro Marker.</p>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,21 @@
<!-- /docs/hooks/useLineData.md -->
<h1 id="uselinedata.js">📊 useLineData.js</h1>
<p>Lädt Linienstatusdaten (Farben, Tooltips) aus zwei Webservices in
Redux und bereitet sie auf.</p>
<h2 id="rückgabe">Rückgabe</h2>
<ul>
<li><code>lineColors</code>: Farben pro Linie basierend auf Status</li>
<li><code>tooltipContents</code>: HTML-Tooltip pro Modul/Station</li>
</ul>
<h2 id="datenquellen">Datenquellen</h2>
<ul>
<li><code>fetchGisLinesThunk()</code> (Struktur)</li>
<li><code>fetchGisLinesStatusThunk()</code> (Statusdaten)</li>
</ul>
<h2 id="intern">Intern</h2>
<ul>
<li>Nutzt Map <code>valueMap</code>, um Messwert, Schleifenwert,
Meldungen zu gruppieren</li>
</ul>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,21 @@
<!-- /docs/hooks/useMapComponentState.md -->
<h1 id="usemapcomponentstate.js">🧠 useMapComponentState.js</h1>
<p>Sammelt zentrale UI-Zustände und Redux-Daten für die
<code>MapComponent</code>.</p>
<h2 id="rückgabe">Rückgabe</h2>
<ul>
<li>POI-Typen + Ladezustand</li>
<li><code>deviceName</code> (z.B. erstes Gerät)</li>
<li><code>locationDeviceData</code></li>
<li><code>priorityConfig</code></li>
<li><code>menuItemAdded</code>, <code>setMenuItemAdded</code></li>
<li>Sichtbarkeit des POI-Layers</li>
</ul>
<h2 id="redux">Redux</h2>
<ul>
<li><code>fetchPoiTypThunk</code>,
<code>fetchGisStationsStaticDistrictThunk</code>,
<code>fetchPriorityConfigThunk</code></li>
</ul>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,13 @@
<!-- /docs/hooks/useMarkerLayers.md -->
<h1 id="usemarkerlayers.js">📍 useMarkerLayers.js</h1>
<p>Steuert das Hinzufügen oder Entfernen von Markern in ein
Leaflet-Map-Layer.</p>
<h2 id="verwendung">Verwendung</h2>
<div class="sourceCode" id="cb1"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">useMarkerLayers</span>(map<span class="op">,</span> gmaMarkers<span class="op">,</span> <span class="st">&quot;GMA&quot;</span>)<span class="op">;</span></span></code></pre></div>
<h2 id="redux">Redux</h2>
<ul>
<li>Liest <code>mapLayersVisibility</code> aus dem Store</li>
<li>Reagiert automatisch auf Änderungen</li>
</ul>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,20 @@
<!-- /docs/hooks/usePolylineTooltipLayer.md -->
<h1 id="usepolylinetooltiplayer.js">💬 usePolylineTooltipLayer.js</h1>
<p>Initialisiert und steuert Polylinien + Tooltip-Verhalten für
Linienmessdaten.</p>
<h2 id="funktion">Funktion</h2>
<ul>
<li>Nutzt <code>setupPolylines(...)</code> zur Marker- und
Linienerstellung</li>
<li>Tooltip-Anzeige bei <code>mouseover</code>, dynamisch
positioniert</li>
<li>Entfernt alte Marker und Polylinien automatisch</li>
</ul>
<h2 id="parameter-gekürzt">Parameter (gekürzt)</h2>
<ul>
<li><code>map</code>, <code>markers</code>, <code>setMarkers</code>,
<code>setPolylines</code>, <code>linePositions</code>,
<code>tooltipContents</code>, <code>lineColors</code>, etc.</li>
</ul>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,53 @@
<!-- /docs/nssm-exe-installation.md -->
<div class="sourceCode" id="cb1"><pre
class="sourceCode markdown"><code class="sourceCode markdown"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ss">- </span>Sicherstellen, dass <span class="in">`nssm.exe`</span> in <span class="in">`C:\inetpub\wwwroot\talas5\nodeMap\`</span> vorhanden ist</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ss">- </span>Als Administrator Eingabeaufforderung oder PowerShell öffnen</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="ss">- </span>Navigiere zu dem NodeMap Projekt Verzeichnis:</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="in">```shell</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> <span class="ex">C:\Users\Administrator</span><span class="op">&gt;</span>cd C:<span class="dt">\i</span>netpub<span class="dt">\w</span>wwroot<span class="dt">\t</span>alas5<span class="dt">\n</span>odeMap</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> <span class="in">```</span></span></code></pre></div>
<ul>
<li><p>Befehl zum Erstellen eines Dienstes: Führen Sie den folgenden
Befehl aus, um einen neuen Dienst zu erstellen:</p>
<pre class="shell"><code>.\nssm.exe install NodeMapService</code></pre>
<p>Nachdem Sie diesen Befehl ausgeführt haben, öffnet sich ein
NSSM-Dialogfenster.</p>
<figure>
<img src="/docs/screenshots/nssm-service-editor.png"
alt="NSSM service editor" />
<figcaption aria-hidden="true">NSSM service editor</figcaption>
</figure>
<p><strong>Dienstkonfiguration:</strong> In dem geöffneten
NSSM-Dialogfenster müssen Sie einige Parameter angeben:</p>
<ul>
<li><p><strong>Path:</strong> Der Pfad zur ausführbaren Datei, die der
Dienst ausführen soll.</p>
<pre class="shell"><code>C:\inetpub\wwwroot\talas5\nodeMap\StartNodeApp.bat</code></pre></li>
<li><p><strong>Startup directory:</strong> Das Verzeichnis, in dem die
Anwendung gestartet werden soll.</p>
<pre class="shell"><code>C:\inetpub\wwwroot\talas5\nodeMap</code></pre></li>
<li><p><strong>Arguments:</strong> kann leer gelassen werden.</p></li>
</ul></li>
<li><p>Dienst starten: Sobald der Dienst erstellt wurde, können Sie ihn
starten. Das können Sie entweder über die Eingabeaufforderung oder über
die Diensteverwaltung von Windows tun.</p>
<figure>
<img src="/docs/screenshots/Dienst-beenden.png" alt="nodeMap Dienst" />
<figcaption aria-hidden="true">nodeMap Dienst</figcaption>
</figure>
<p>Um den Dienst über die Eingabeaufforderung zu starten, verwenden Sie
den folgenden Befehl:</p>
<pre class="shell"><code>nssm.exe start DienstName</code></pre></li>
</ul>
<hr />
<ul>
<li><p><strong>Dienst bearbeiten:</strong></p>
<pre class="shell"><code>nssm.exe edit NodeMapService</code></pre></li>
<li><p><strong>Dienst entfernen:</strong></p>
<pre class="shell"><code>nssm.exe remove NodeMapService confirm</code></pre>
<p>dauert bis 1 Minute</p></li>
</ul>
<pre><code></code></pre>
<hr />
<p><a href="README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,5 @@
<h1 id="übersicht-docspages">📄 Übersicht: docs/pages</h1>
<ul>
<li><a href="_app.md">_app</a></li>
<li><a href="index.md">index</a></li>
</ul>

View File

@@ -0,0 +1,21 @@
<!-- /docs/pages/_app.md -->
<h1 id="app.js">🌐 _app.js</h1>
<p>Diese Datei stellt die Haupt-Wrap-Komponente der Next.js-App
dar.<br />
Sie initialisiert globale Provider wie den Redux Store.</p>
<h2 id="features">Features</h2>
<ul>
<li>Importiert globales CSS (<code>styles/global.css</code>)</li>
<li>Bindet Redux <code>Provider</code> um alle Seiten-Komponenten</li>
<li>Ermöglicht Zugriff auf Store in allen Seiten</li>
</ul>
<h2 id="struktur">Struktur</h2>
<div class="sourceCode" id="cb1"><pre
class="sourceCode jsx"><code class="sourceCode javascriptreact"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">&lt;Provider</span> <span class="ot">store</span><span class="op">=</span><span class="va">{</span>store<span class="va">}</span><span class="fu">&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="fu">&lt;Component</span> <span class="va">{</span><span class="op">...</span>pageProps<span class="va">}</span> <span class="fu">/&gt;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="fu">&lt;/Provider&gt;</span></span></code></pre></div>
<h2 id="pfad">Pfad</h2>
<div class="sourceCode" id="cb2"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">/pages/_app.js</span></span></code></pre></div>
<hr />
<p><a href="../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,4 @@
<h1 id="übersicht-docspagesapi">📄 Übersicht: docs/pages/api</h1>
<ul>
<li><a href="%5B...path%5D.md">[…path]</a></li>
</ul>

View File

@@ -0,0 +1,37 @@
<!-- /docs/pages/api/[...path].md -->
<h1 id="path.js">🌐 […path].js</h1>
<p>Next.js API-Proxy-Handler mit
<code>http-proxy-middleware</code>.<br />
Dient als Middleware zur Weiterleitung von API-Requests an das Backend
(z.B. Raspberry Pi oder Entwicklungsserver).</p>
<hr />
<h2 id="funktion">🔧 Funktion</h2>
<ul>
<li>Leitet alle Requests von <code>/api/...</code> an das definierte
<code>target</code> weiter</li>
<li>Entfernt <code>/api</code> aus dem URL-Pfad</li>
<li>Erlaubt Cross-Origin Requests mit
<code>changeOrigin: true</code></li>
</ul>
<hr />
<h2 id="ziel-logik">Ziel-Logik</h2>
<div class="sourceCode" id="cb1"><pre class="sourceCode js"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> mode <span class="op">=</span> <span class="bu">process</span><span class="op">.</span><span class="at">env</span><span class="op">.</span><span class="at">NEXT_PUBLIC_API_PORT_MODE</span><span class="op">;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> target <span class="op">=</span> mode <span class="op">===</span> <span class="st">&quot;dev&quot;</span> <span class="op">?</span> <span class="st">&quot;http://localhost:80&quot;</span> <span class="op">:</span> <span class="st">&quot;http://localhost&quot;</span><span class="op">;</span></span></code></pre></div>
<hr />
<h2 id="beispiel">Beispiel</h2>
<ul>
<li>Frontend-Request:
<code>GET /api/GisStationsStaticDistrict</code></li>
<li>Weitergeleitet an:
<code>GET http://localhost:80/GisStationsStaticDistrict</code></li>
</ul>
<hr />
<h2 id="besonderheiten">Besonderheiten</h2>
<ul>
<li>Ermöglicht portunabhängige Proxy-Nutzung über <code>.env</code></li>
<li>Setzt <code>logLevel: "debug"</code> zur Diagnose</li>
</ul>
<hr />
<h2 id="pfad">Pfad</h2>
<div class="sourceCode" id="cb2"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">/pages/api/[...path].js</span></span></code></pre></div>

View File

@@ -0,0 +1,5 @@
<h1 id="übersicht-docspagesapitalas_v5_db">📄 Übersicht:
docs/pages/api/talas_v5_DB</h1>
<ul>
<li><a href="priorityConfig.md">priorityConfig</a></li>
</ul>

View File

@@ -0,0 +1,6 @@
<h1 id="übersicht-docspagesapitalas_v5_dbarea">📄 Übersicht:
docs/pages/api/talas_v5_DB/area</h1>
<ul>
<li><a href="readArea.md">readArea</a></li>
<li><a href="updateArea.md">updateArea</a></li>
</ul>

View File

@@ -0,0 +1,40 @@
<!-- /docs/pages/api/talas_v5_DB/area/readArea.md -->
<h1 id="readarea.js">📥 readArea.js</h1>
<p>Liest Bereichskoordinaten (<code>location_coordinates</code>) aus der
Datenbank basierend auf <code>idMaps</code> (und optional
<code>idLocation</code>).</p>
<h2 id="methode">Methode</h2>
<ul>
<li><code>GET</code></li>
</ul>
<h2 id="url-parameter">URL-Parameter</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>m</code></td>
<td>Karten-ID (entspricht <code>idMaps</code>)</td>
</tr>
<tr>
<td><code>idLocation</code></td>
<td>(optional) ID eines bestimmten Bereichs</td>
</tr>
</tbody>
</table>
<h2 id="verhalten">Verhalten</h2>
<ul>
<li>Joint <code>location</code>, <code>location_coordinates</code> und
<code>area</code>-Tabelle</li>
<li>Gibt strukturierte Daten mit <code>x</code>, <code>y</code>,
<code>location_name</code>, <code>area_name</code> zurück</li>
<li>Nutzt MySQL-Pool (<code>getPool()</code>)</li>
</ul>
<h2 id="beispiel">Beispiel</h2>
<pre class="http"><code>GET /api/talas_v5_DB/area/readArea?m=3</code></pre>
<hr />
<p><a href="../../../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,32 @@
<!-- /docs/pages/api/talas_v5_DB/area/updateArea.md -->
<h1 id="updatearea.js">📤 updateArea.js</h1>
<p>Aktualisiert die Koordinaten eines Bereichs
(<code>location_coordinates</code>) basierend auf
<code>idLocation</code> und <code>idMap</code>.</p>
<h2 id="methode">Methode</h2>
<ul>
<li><code>PUT</code></li>
</ul>
<h2 id="request-body">Request-Body</h2>
<div class="sourceCode" id="cb1"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;idLocation&quot;</span><span class="fu">:</span> <span class="dv">12</span><span class="fu">,</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;idMap&quot;</span><span class="fu">:</span> <span class="dv">3</span><span class="fu">,</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;x&quot;</span><span class="fu">:</span> <span class="fl">53.21421</span><span class="fu">,</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;y&quot;</span><span class="fu">:</span> <span class="fl">8.43212</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<h2 id="verhalten">Verhalten</h2>
<ul>
<li>Führt
<code>UPDATE location_coordinates SET x=?, y=? WHERE idLocation=? AND idMaps=?</code></li>
<li>Gibt bei Erfolg <code>success: true</code> zurück</li>
<li>Nutzt MySQL-Pool und <code>connection.release()</code></li>
</ul>
<h2 id="fehlerbehandlung">Fehlerbehandlung</h2>
<ul>
<li>400: Fehlende Daten</li>
<li>404: Kein Eintrag gefunden</li>
<li>500: Interner Fehler</li>
</ul>
<hr />
<p><a href="../../../../README.md">Zurück zur Übersicht</a></p>

View File

@@ -0,0 +1,6 @@
<h1 id="übersicht-docspagesapitalas_v5_dbdevice">📄 Übersicht:
docs/pages/api/talas_v5_DB/device</h1>
<ul>
<li><a href="getAllStationsNames.md">getAllStationsNames</a></li>
<li><a href="getDevices.md">getDevices</a></li>
</ul>

Some files were not shown because too many files have changed in this diff Show More