Compare commits
45 Commits
fff2754b14
...
feat/minim
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f390f93293 | ||
|
|
28dcb284bf | ||
|
|
4f0527e8a9 | ||
|
|
3d0ce4a2b4 | ||
|
|
76280b365b | ||
|
|
c112ec2da4 | ||
|
|
7faee5fd79 | ||
|
|
e52b0cc520 | ||
|
|
4a42c428f0 | ||
|
|
1d3d04d49c | ||
|
|
dd9980409c | ||
|
|
ea6d71a4f5 | ||
|
|
13ca1cece0 | ||
|
|
f22bb4b232 | ||
|
|
bfd091b1b1 | ||
|
|
81b6379895 | ||
|
|
42ca88d27e | ||
|
|
fdb70d892c | ||
|
|
73e9c63e36 | ||
|
|
e520207526 | ||
|
|
2e5acf9327 | ||
|
|
cdfdd3d6cf | ||
|
|
5b86d5293b | ||
|
|
31c770f778 | ||
|
|
051dd4c306 | ||
|
|
995f084e15 | ||
|
|
eaacec71da | ||
|
|
6bc2e16657 | ||
|
|
1208024f76 | ||
|
|
369f29a769 | ||
|
|
d166b2468d | ||
|
|
59c8680c23 | ||
|
|
1a046f8212 | ||
|
|
e35216daf5 | ||
|
|
91ad47166f | ||
|
|
3a9b436352 | ||
|
|
7b881e80c2 | ||
|
|
cc19a0a466 | ||
|
|
f200d0bb20 | ||
|
|
75a0ab000f | ||
|
|
4d2a94ffea | ||
|
|
239ad82e46 | ||
|
|
a2d3338624 | ||
|
|
598acb8441 | ||
|
|
f8512c485e |
@@ -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.350
|
||||
NEXT_PUBLIC_APP_VERSION=1.1.396
|
||||
|
||||
@@ -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.350
|
||||
NEXT_PUBLIC_APP_VERSION=1.1.396
|
||||
|
||||
25
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
echo "🔄 Version wird automatisch erhöht (bumpVersion.js)..."
|
||||
|
||||
# Version automatisch erhöhen
|
||||
|
||||
195
README.confluence
Normal 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. v18–20)
|
||||
* 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
BIN
README.pdf
Normal file
@@ -74,3 +74,32 @@ die Daten von DB auch mit WebSocket gelöst werden
|
||||
|
||||
28.07.2025 IdSystem 11 GMA Glätemeldeanlagen, werden neu neu laden das Browser nich mehr geladen in
|
||||
DB maps idsystem ändern und testen
|
||||
|
||||
# 12.09.2025
|
||||
|
||||
Die aktuelle Ansicht ist bei kleineren Auflösungen unübersichtlich bzw. es wird zuviel von der
|
||||
eigentlichen Karte verdeckt. Unquittierter Alarm, critical
|
||||
|
||||
Zu Marker zoomen
|
||||
|
||||
Station suchen
|
||||
|
||||
- [ ] TODO: Unquittierter Alarm, critical 🚨 Alarm
|
||||
- [ ] TODO: Zu Marker zoomen: Dropdown-Menu Station auswählen und hinein zoomen bis zu ausgewählte
|
||||
in einem betimmten Zoom-Stufe der Leaflet (OSM) Station mit flyto in Leaflet 📍 POI
|
||||
- [ ] TODO: Station suchen: CoordinateInput.js Modal soll über einem Icon 'Suche / Lupe' oben rechts
|
||||
eingeblendet und ausgeblendet um mehr von der Karte zu sehen 🔍 Suche
|
||||
- [ ] TODO: Editiermodus: EditMode Stift Icon aktivieren und deaktivieren um POI Position ändern zu
|
||||
können wenn der User Berechtigung hat ✏️ Edit
|
||||
- [ ] TODO: Vergrössern: Maximieren Icon Button um rauszoomen zu einem bestimmten Bereich, z.B.
|
||||
Deutschland Karte im Fenster sichtbar ⬜ Fenster maximieren
|
||||
|
||||
- [ ] TODO: Ebenen (Openstreetmap): Stack/Stapel/Ebenen Icon um den Ansicht der Karten zu ändern 🗂️
|
||||
Stapel
|
||||
|
||||
- [ ] TODO: Menü öffenen mit Kiste der Systeme: MapLayerControlPanel.js Modal soll über einem Icon
|
||||
'Hamburger menu' oben rechts eingeblendet und ausgeblendet um mehr von der Karte zu sehen ☰
|
||||
Hamburger-Menü
|
||||
- [ ] TODO: Info Karte: VersionInfoModal.js Modal soll über einem Icon 'Info' oben rechts
|
||||
eingeblendet und ausgeblendet um mehr von der Karte zu sehen ℹ️ Info
|
||||
https://www.openstreetmap.org/#map=13/51.80097/9.33495&layers=P
|
||||
18
components/icons/material-symbols/AlarmIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const AlarmIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="red"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M6 6.9L3.87 4.78l1.41-1.41L7.4 5.5zM13 1v3h-2V1zm7.13 3.78L18 6.9l-1.4-1.4l2.12-2.13zM4.5 10.5v2h-3v-2zm15 0h3v2h-3zM6 20h12a2 2 0 0 1 2 2H4a2 2 0 0 1 2-2m6-15a6 6 0 0 1 6 6v8H6v-8a6 6 0 0 1 6-6"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default AlarmIcon;
|
||||
18
components/icons/material-symbols/EditIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const EditIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default EditIcon;
|
||||
18
components/icons/material-symbols/EditOffIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const EditOffIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L18 21l-3-3m-12.728-.364A9 9 0 015.636 5.636m0 0L3 3l3 3m9.364 9.364L18 21M5.636 5.636L3 3"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default EditOffIcon;
|
||||
18
components/icons/material-symbols/ExpandIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const ExpandIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default ExpandIcon;
|
||||
18
components/icons/material-symbols/InfoIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const InfoIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default InfoIcon;
|
||||
19
components/icons/material-symbols/MapMarkerIcon.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const MapMarkerIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="rgb(0, 174, 239)"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25s-7.5-4.108-7.5-11.25a7.5 7.5 0 1115 0z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default MapMarkerIcon;
|
||||
18
components/icons/material-symbols/MenuIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const MenuIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default MenuIcon;
|
||||
14
components/icons/material-symbols/MinusIcon.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const MinusIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
strokeWidth="1.5"
|
||||
stroke="currentColor"
|
||||
className={className}
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M5 12h14" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default MinusIcon;
|
||||
14
components/icons/material-symbols/PlusIcon.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const PlusIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
strokeWidth="1.5"
|
||||
stroke="currentColor"
|
||||
className={className}
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 5v14M5 12h14" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default PlusIcon;
|
||||
18
components/icons/material-symbols/SearchIcon.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const SearchIcon = ({ className = "h-8 w-8" }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="rgb(0, 174, 239)"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default SearchIcon;
|
||||
@@ -6,7 +6,17 @@ import "leaflet-contextmenu/dist/leaflet.contextmenu.css";
|
||||
import "leaflet-contextmenu";
|
||||
import "leaflet.smooth_marker_bouncing";
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import { InformationCircleIcon } from "@heroicons/react/20/solid";
|
||||
import { Icon } from "@iconify/react";
|
||||
import EditIcon from "@/components/icons/material-symbols/EditIcon";
|
||||
import EditOffIcon from "@/components/icons/material-symbols/EditOffIcon";
|
||||
import SearchIcon from "@/components/icons/material-symbols/SearchIcon";
|
||||
import MenuIcon from "@/components/icons/material-symbols/MenuIcon";
|
||||
import InfoIcon from "@/components/icons/material-symbols/InfoIcon";
|
||||
import AlarmIcon from "@/components/icons/material-symbols/AlarmIcon";
|
||||
import MapMarkerIcon from "@/components/icons/material-symbols/MapMarkerIcon";
|
||||
import ExpandIcon from "@/components/icons/material-symbols/ExpandIcon";
|
||||
import PlusIcon from "@/components/icons/material-symbols/PlusIcon";
|
||||
import MinusIcon from "@/components/icons/material-symbols/MinusIcon";
|
||||
import PoiUpdateModal from "@/components/pois/poiUpdateModal/PoiUpdateModal.js";
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
import plusRoundIcon from "../icons/devices/overlapping/PlusRoundIcon.js";
|
||||
@@ -24,8 +34,10 @@ 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";
|
||||
//----------Daten aus API--------------------
|
||||
import { fetchPoiDataService } from "@/services/database/pois/fetchPoiDataByIdService.js";
|
||||
import AddPOIModal from "@/components/pois/AddPOIModal.js";
|
||||
@@ -39,6 +51,8 @@ import { setSelectedPoi } from "@/redux/slices/database/pois/selectedPoiSlice.js
|
||||
import { setDisabled } from "@/redux/slices/database/polylines/polylineEventsDisabledSlice.js";
|
||||
import { setMapId, setUserId } from "@/redux/slices/urlParameterSlice";
|
||||
import { selectMapLayersState, setLayerVisibility } from "@/redux/slices/mapLayersSlice";
|
||||
import { setSelectedArea } from "@/redux/slices/selectedAreaSlice";
|
||||
import { incrementZoomTrigger } from "@/redux/slices/zoomTriggerSlice";
|
||||
import { setCurrentPoi } from "@/redux/slices/database/pois/currentPoiSlice.js";
|
||||
import { selectGisLines } from "@/redux/slices/database/polylines/gisLinesSlice";
|
||||
import { selectGisLinesStatus } from "@/redux/slices/webservice/gisLinesStatusSlice";
|
||||
@@ -119,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) || {};
|
||||
@@ -129,6 +143,41 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
const { data: gisLinesStatusData, status: statusGisLinesStatus } = useSelector(
|
||||
selectGisLinesStatusFromWebservice
|
||||
);
|
||||
|
||||
// Alarm Status und Link dynamisch aus GisStationsStaticDistrict
|
||||
const gisStationsStatusDistrict = useSelector(state => state.gisStationsStatusDistrict.data);
|
||||
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);
|
||||
@@ -147,10 +196,69 @@ 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(() => {
|
||||
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
|
||||
const [oms, setOms] = useState(null); // State für OMS-Instanz
|
||||
// Sichtbarkeit der App-Info-Karte (unten links)
|
||||
const [showAppInfoCard, setShowAppInfoCard] = useState(() => {
|
||||
try {
|
||||
const v = localStorage.getItem("showAppInfoCard");
|
||||
return v === null ? true : v === "true";
|
||||
} catch (_) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
// Sichtbarkeit des Layer-Kontrollpanels (oben rechts)
|
||||
const [showLayersPanel, setShowLayersPanel] = useState(() => {
|
||||
try {
|
||||
const v = localStorage.getItem("showLayersPanel");
|
||||
return v === null ? true : v === "true";
|
||||
} catch (_) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
// Base-Map Panel wurde entfernt
|
||||
// Sichtbarkeit der Koordinaten-Suche (Lupe)
|
||||
const [showCoordinateInput, setShowCoordinateInput] = useState(() => {
|
||||
try {
|
||||
const v = localStorage.getItem("showCoordinateInput");
|
||||
return v === null ? false : v === "true";
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// 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
|
||||
@@ -188,6 +296,14 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
const [popupCoordinates, setPopupCoordinates] = useState(null);
|
||||
const [popupVisible, setPopupVisible] = useState(false);
|
||||
const [poiData, setPoiData] = useState([]);
|
||||
// Edit mode state mirrors MapLayersControlPanel's behavior
|
||||
const [editMode, setEditMode] = useState(() => {
|
||||
try {
|
||||
return localStorage.getItem("editMode") === "true";
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const openVersionInfoModal = () => {
|
||||
setShowVersionInfoModal(true);
|
||||
@@ -209,6 +325,32 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Persistiere Sichtbarkeit der App-Info-Karte
|
||||
useEffect(() => {
|
||||
try {
|
||||
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 {
|
||||
localStorage.setItem("showLayersPanel", String(showLayersPanel));
|
||||
} catch (_) {}
|
||||
}, [showLayersPanel]);
|
||||
// Persist-Logik für Base-Map Panel entfernt
|
||||
// Persistiere Sichtbarkeit der Koordinaten-Suche
|
||||
useEffect(() => {
|
||||
try {
|
||||
localStorage.setItem("showCoordinateInput", String(showCoordinateInput));
|
||||
} catch (_) {}
|
||||
}, [showCoordinateInput]);
|
||||
|
||||
//--------------------------------------------
|
||||
|
||||
const handleCoordinatesSubmit = coords => {
|
||||
@@ -981,6 +1123,29 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
}, [GisSystemStatic, mapLayersVisibility, dispatch]);
|
||||
|
||||
//---------------------------------------------
|
||||
//--------------------------------------------
|
||||
// Expand handler (same behavior as MapLayersControlPanel expand icon)
|
||||
const handleExpandClick = () => {
|
||||
dispatch(setSelectedArea("Station wählen"));
|
||||
dispatch(incrementZoomTrigger());
|
||||
};
|
||||
|
||||
// Toggle edit mode (same logic as EditModeToggle component)
|
||||
const hasEditRight = Array.isArray(userRights)
|
||||
? userRights.includes?.(56) || userRights.some?.(r => r?.IdRight === 56)
|
||||
: false;
|
||||
const toggleEditMode = () => {
|
||||
if (!hasEditRight) return;
|
||||
const next = !editMode;
|
||||
setEditMode(next);
|
||||
try {
|
||||
localStorage.setItem("editMode", String(next));
|
||||
} catch (_) {}
|
||||
if (typeof window !== "undefined") {
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------
|
||||
return (
|
||||
<>
|
||||
@@ -1035,31 +1200,136 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{GisStationsStaticDistrict && GisStationsStaticDistrict.Points?.length > 0 && (
|
||||
<MapLayersControlPanel
|
||||
className="z-50"
|
||||
handlePolylineCheckboxChange={handlePolylineCheckboxChange}
|
||||
/>
|
||||
)}
|
||||
{GisStationsStaticDistrict &&
|
||||
GisStationsStaticDistrict.Points?.length > 0 &&
|
||||
showLayersPanel &&
|
||||
!showAreaDropdown && (
|
||||
<MapLayersControlPanel
|
||||
className="z-50"
|
||||
handlePolylineCheckboxChange={handlePolylineCheckboxChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
<CoordinateInput onCoordinatesSubmit={handleCoordinatesSubmit} />
|
||||
{showCoordinateInput && <CoordinateInput onCoordinatesSubmit={handleCoordinatesSubmit} />}
|
||||
<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 und Link vorhanden */}
|
||||
<AlarmIndicator hasAlarm={hasActiveAlarm} alarmLink={alarmLink} alarmText={alarmText} />
|
||||
{/* Marker-Icon (line-md) */}
|
||||
<button
|
||||
onClick={() => setOverlay(prev => (prev === "area" ? null : "area"))}
|
||||
aria-label="Marker"
|
||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||
title="Marker"
|
||||
>
|
||||
<MapMarkerIcon className="h-8 w-8" />
|
||||
</button>
|
||||
{/*Lupe: Koordinatensuche ein-/ausblenden */}
|
||||
<button
|
||||
onClick={() => setOverlay(prev => (prev === "coord" ? null : "coord"))}
|
||||
aria-label={
|
||||
showCoordinateInput ? "Koordinatensuche ausblenden" : "Koordinatensuche einblenden"
|
||||
}
|
||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||
title={
|
||||
showCoordinateInput ? "Koordinatensuche ausblenden" : "Koordinatensuche einblenden"
|
||||
}
|
||||
>
|
||||
<SearchIcon className="h-8 w-8" />
|
||||
</button>
|
||||
<button
|
||||
onClick={toggleEditMode}
|
||||
aria-label={editMode ? "Bearbeitungsmodus deaktivieren" : "Bearbeitungsmodus aktivieren"}
|
||||
className={`rounded-full shadow p-1 ${
|
||||
hasEditRight
|
||||
? "bg-white/90 hover:bg-white"
|
||||
: "bg-white/60 cursor-not-allowed opacity-50"
|
||||
}`}
|
||||
title={
|
||||
hasEditRight
|
||||
? editMode
|
||||
? "Bearbeitungsmodus deaktivieren"
|
||||
: "Bearbeitungsmodus aktivieren"
|
||||
: "Keine Bearbeitungsrechte"
|
||||
}
|
||||
disabled={!hasEditRight}
|
||||
>
|
||||
{editMode ? <EditOffIcon className="h-8 w-8" /> : <EditIcon className="h-8 w-8" />}
|
||||
</button>
|
||||
{/* Expand: Karte auf Standardansicht */}
|
||||
<button
|
||||
onClick={handleExpandClick}
|
||||
aria-label="Karte auf Standardansicht"
|
||||
className="rounded-full bg-white/90 hover:bg-white shadow p-1 "
|
||||
title="Karte auf Standardansicht"
|
||||
>
|
||||
<ExpandIcon className="h-8 w-8" />
|
||||
</button>
|
||||
{/* Lupe: Koordinaten-Suche ein-/ausblenden */}
|
||||
<button
|
||||
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"}
|
||||
>
|
||||
<MenuIcon className="h-8 w-8" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
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"}
|
||||
>
|
||||
<InfoIcon
|
||||
className="h-8 w-8 pr-1"
|
||||
title={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
|
||||
/>
|
||||
</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} />
|
||||
|
||||
<div className="absolute bottom-3 left-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>
|
||||
<br />
|
||||
<span className="text-black text-lg">Version {appVersion}</span>
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={openVersionInfoModal}>
|
||||
<InformationCircleIcon className="text-blue-900 h-8 w-8 pr-1" title="Weitere Infos" />
|
||||
</button>
|
||||
{showAppInfoCard && (
|
||||
<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>
|
||||
<br />
|
||||
<span className="text-black text-lg">Version {appVersion}</span>
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={openVersionInfoModal}>
|
||||
<InfoIcon className="h-8 w-8 pr-1" title="Weitere Infos" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<VersionInfoModal
|
||||
showVersionInfoModal={showVersionInfoModal}
|
||||
closeVersionInfoModal={closeVersionInfoModal}
|
||||
|
||||
53
components/uiWidgets/AlarmIndicator.js
Normal 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;
|
||||
62
components/uiWidgets/AlarmIndicator.module.css
Normal 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;
|
||||
}
|
||||
}
|
||||
79
components/uiWidgets/AreaDropdown.js
Normal file
@@ -0,0 +1,79 @@
|
||||
// /components/uiWidgets/AreaDropdown.js
|
||||
import React, { useEffect, useMemo } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { setSelectedArea } from "@/redux/slices/selectedAreaSlice";
|
||||
import { selectGisStationsStaticDistrict } from "@/redux/slices/webservice/gisStationsStaticDistrictSlice";
|
||||
import { selectGisSystemStatic } from "@/redux/slices/webservice/gisSystemStaticSlice";
|
||||
|
||||
/**
|
||||
* Kleines Dropdown zur Auswahl der Station (Area_Name),
|
||||
* nutzt dieselbe Datenquelle wie das MapLayersControlPanel.
|
||||
*/
|
||||
const AreaDropdown = ({ onClose }) => {
|
||||
const dispatch = useDispatch();
|
||||
const GisStationsStaticDistrict = useSelector(selectGisStationsStaticDistrict) || {};
|
||||
const GisSystemStatic = useSelector(selectGisSystemStatic) || [];
|
||||
|
||||
// Erlaubte Systeme: Allow === 1 und Map === 1
|
||||
const allowedSystems = useMemo(() => {
|
||||
return new Set(
|
||||
(Array.isArray(GisSystemStatic) ? GisSystemStatic : [])
|
||||
.filter(sys => sys.Allow === 1 && sys.Map === 1)
|
||||
.map(sys => sys.IdSystem)
|
||||
);
|
||||
}, [GisSystemStatic]);
|
||||
|
||||
// Uniqe Areas basierend auf Allowed Systems
|
||||
const areaOptions = useMemo(() => {
|
||||
const points = GisStationsStaticDistrict?.Points || [];
|
||||
const seen = new Set();
|
||||
const filtered = points.filter(p => {
|
||||
if (!p?.Area_Name) return false;
|
||||
if (!allowedSystems.has(p.System)) return false;
|
||||
if (seen.has(p.Area_Name)) return false;
|
||||
seen.add(p.Area_Name);
|
||||
return true;
|
||||
});
|
||||
return filtered.map(p => ({ label: p.Area_Name, value: p.IdLD }));
|
||||
}, [GisStationsStaticDistrict, allowedSystems]);
|
||||
|
||||
const handleChange = e => {
|
||||
const selectedIndex = e.target.options.selectedIndex;
|
||||
const label = e.target.options[selectedIndex].text;
|
||||
dispatch(setSelectedArea(label));
|
||||
onClose?.();
|
||||
};
|
||||
|
||||
// Schließe mit ESC
|
||||
useEffect(() => {
|
||||
const onKey = e => {
|
||||
if (e.key === "Escape") onClose?.();
|
||||
};
|
||||
window.addEventListener("keydown", onKey);
|
||||
return () => window.removeEventListener("keydown", onKey);
|
||||
}, [onClose]);
|
||||
|
||||
return (
|
||||
<div className="absolute top-16 right-3 w-72 z-50 bg-white rounded-lg shadow-md">
|
||||
<div className="flex flex-col gap-4 p-4">
|
||||
<div className="text-sm font-semibold mb-2">Station wählen</div>
|
||||
<select
|
||||
onChange={handleChange}
|
||||
className="border p-2 rounded w-full"
|
||||
defaultValue="__default__"
|
||||
>
|
||||
<option value="__default__" disabled>
|
||||
Bitte wählen…
|
||||
</option>
|
||||
{areaOptions.map(opt => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AreaDropdown;
|
||||
@@ -4,7 +4,7 @@ import React, { useState } from "react";
|
||||
const CoordinateInput = ({ onCoordinatesSubmit }) => {
|
||||
const [coordinates, setCoordinates] = useState("");
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
const handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
if (onCoordinatesSubmit) {
|
||||
onCoordinatesSubmit(coordinates);
|
||||
@@ -12,9 +12,18 @@ 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">
|
||||
<input type="text" placeholder="Koordinaten eingeben (lat,lng)" value={coordinates} onChange={(e) => setCoordinates(e.target.value)} className="border p-2 rounded w-full mb-2" />
|
||||
<button type="submit" className="bg-blue-500 text-white p-2 rounded w-full hover:bg-blue-600">
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="absolute top-16 right-3 z-50 bg-white rounded-lg shadow-md p-4 w-72"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Koordinaten eingeben (lat,lng)"
|
||||
value={coordinates}
|
||||
onChange={e => setCoordinates(e.target.value)}
|
||||
className="border p-2 rounded w-full mb-2"
|
||||
/>
|
||||
<button type="submit" className="bg-littwin-blue text-white p-2 rounded w-full ">
|
||||
Zu Marker zoomen
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -31,7 +31,7 @@ const VersionInfoModal = ({ showVersionInfoModal, closeVersionInfoModal, APP_VER
|
||||
</p>
|
||||
<button
|
||||
onClick={closeVersionInfoModal}
|
||||
className="mt-4 bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-700 mx-auto block"
|
||||
className="mt-4 bg-littwin-blue text-white px-4 py-2 rounded mx-auto block"
|
||||
>
|
||||
Schließen
|
||||
</button>
|
||||
|
||||
15
components/uiWidgets/alarm-indicator-fastpulse.css
Normal 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;
|
||||
}
|
||||
163
components/uiWidgets/baseMapPanel/BaseMapPanel.js
Normal file
@@ -0,0 +1,163 @@
|
||||
// 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";
|
||||
|
||||
// Minimal, safe defaults (no API key required). You can extend via config later.
|
||||
const DEFAULT_BASE_LAYERS = [
|
||||
{
|
||||
id: "osm-standard",
|
||||
name: "Standard",
|
||||
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
minZoom: 0,
|
||||
maxZoom: 19,
|
||||
},
|
||||
{
|
||||
id: "osm-humanitarian",
|
||||
name: "Humanitarian",
|
||||
url: "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png",
|
||||
attribution: "© OpenStreetMap contributors, Humanitarian OpenStreetMap Team",
|
||||
minZoom: 0,
|
||||
maxZoom: 19,
|
||||
},
|
||||
{
|
||||
id: "cyclosm",
|
||||
name: "CyclOSM",
|
||||
url: "https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png",
|
||||
attribution: "© OpenStreetMap contributors, CyclOSM",
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
},
|
||||
{
|
||||
id: "carto-light",
|
||||
name: "Carto Light",
|
||||
url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
|
||||
attribution: "© OpenStreetMap contributors, © CARTO",
|
||||
subdomains: "abcd",
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
},
|
||||
];
|
||||
|
||||
function getCurrentTileLayer(map) {
|
||||
let found = null;
|
||||
if (!map) return null;
|
||||
map.eachLayer(layer => {
|
||||
if (!found && layer instanceof L.TileLayer) {
|
||||
found = layer;
|
||||
}
|
||||
});
|
||||
return found;
|
||||
}
|
||||
|
||||
export default function BaseMapPanel({ map, onSelect, onClose, initialId }) {
|
||||
const [activeId, setActiveId] = useState(initialId || null);
|
||||
const layerCacheRef = useRef({});
|
||||
const bases = useMemo(() => {
|
||||
try {
|
||||
if (typeof window !== "undefined") {
|
||||
const cfg = window.__leafletConfig;
|
||||
if (cfg && cfg.tileSources) {
|
||||
return Object.entries(cfg.tileSources).map(([key, ts]) => ({
|
||||
id: key,
|
||||
name: ts.name || key,
|
||||
url: ts.url,
|
||||
attribution: ts.attribution || "© OpenStreetMap contributors",
|
||||
minZoom: ts.minZoom ?? cfg.minZoom ?? 0,
|
||||
maxZoom: ts.maxZoom ?? cfg.maxZoom ?? 19,
|
||||
subdomains: ts.subdomains,
|
||||
}));
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
return DEFAULT_BASE_LAYERS;
|
||||
}, []);
|
||||
|
||||
const applyBase = id => {
|
||||
if (!map) return;
|
||||
const base = bases.find(b => b.id === id) || bases[0];
|
||||
if (!base) return;
|
||||
|
||||
// Remove current tile layer
|
||||
const current = getCurrentTileLayer(map);
|
||||
if (current) {
|
||||
try {
|
||||
map.removeLayer(current);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// Get or create the new layer
|
||||
let nextLayer = layerCacheRef.current[id];
|
||||
if (!nextLayer) {
|
||||
nextLayer = L.tileLayer(base.url, {
|
||||
attribution: base.attribution,
|
||||
subdomains: base.subdomains || "abc",
|
||||
tileSize: 256,
|
||||
minZoom: base.minZoom ?? 0,
|
||||
maxZoom: base.maxZoom ?? 19,
|
||||
noWrap: true,
|
||||
// Ensure base tiles stay behind overlays
|
||||
zIndex: 1,
|
||||
});
|
||||
layerCacheRef.current[id] = nextLayer;
|
||||
}
|
||||
|
||||
nextLayer.addTo(map);
|
||||
try {
|
||||
if (typeof map.setMinZoom === "function") map.setMinZoom(base.minZoom ?? 0);
|
||||
if (typeof map.setMaxZoom === "function") map.setMaxZoom(base.maxZoom ?? 19);
|
||||
if (typeof window !== "undefined") {
|
||||
window.__tileSourceMinZoom = base.minZoom ?? 0;
|
||||
window.__tileSourceMaxZoom = base.maxZoom ?? 19;
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
setActiveId(id);
|
||||
try {
|
||||
localStorage.setItem("baseMapId", id);
|
||||
} catch (_) {}
|
||||
onSelect && onSelect(id);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const saved = (() => {
|
||||
try {
|
||||
return localStorage.getItem("baseMapId");
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
const targetId = initialId || saved || bases[0]?.id;
|
||||
if (targetId) {
|
||||
applyBase(targetId);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [map]);
|
||||
|
||||
return (
|
||||
<div className="absolute top-16 right-3 z-50 w-64 bg-white rounded-lg shadow-lg p-3">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h3 className="text-sm font-semibold">Map Layers</h3>
|
||||
<button onClick={onClose} aria-label="Schließen" title="Schließen">
|
||||
<Icon icon="material-symbols:close-rounded" className="h-5 w-5 text-gray-700" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
{bases.map(b => (
|
||||
<button
|
||||
key={b.id}
|
||||
onClick={() => applyBase(b.id)}
|
||||
className={`text-left rounded-md border p-2 hover:bg-gray-50 ${
|
||||
activeId === b.id ? "ring-2 ring-blue-500" : ""
|
||||
}`}
|
||||
title={b.name}
|
||||
>
|
||||
<div className="font-medium text-sm">{b.name}</div>
|
||||
<div className="text-[10px] text-gray-500 truncate">{b.url}</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -241,51 +241,42 @@ function MapLayersControlPanel({ handlePolylineCheckboxChange }) {
|
||||
}, [GisStationsStaticDistrict]);
|
||||
//---------------------------
|
||||
|
||||
// Polyline (Kabelstrecken) abhängig von TALAS (system-1)
|
||||
const onPolylineToggle = checked => {
|
||||
if (editMode) return;
|
||||
|
||||
// Wenn Nutzer Kabelstrecken einschaltet, aber TALAS aktuell ausgeblendet ist,
|
||||
// dann TALAS automatisch aktivieren (sofern erlaubt)
|
||||
const talasKey = "system-1";
|
||||
const talasVisible = !!mapLayersVisibility[talasKey];
|
||||
if (checked && isTalasAllowed && !talasVisible) {
|
||||
dispatch(setLayerVisibility({ layer: talasKey, visibility: true }));
|
||||
|
||||
// Persistiere Sichtbarkeit map/user-spezifisch
|
||||
const mapId2 = localStorage.getItem("currentMapId");
|
||||
const userId2 = localStorage.getItem("currentUserId");
|
||||
const mapStorageKey =
|
||||
mapId2 && userId2 ? `mapLayersVisibility_m${mapId2}_u${userId2}` : "mapLayersVisibility";
|
||||
localStorage.setItem(
|
||||
mapStorageKey,
|
||||
JSON.stringify({ ...mapLayersVisibility, [talasKey]: true })
|
||||
);
|
||||
|
||||
// Event feuern wie an anderer Stelle
|
||||
setTimeout(() => {
|
||||
const event = new Event("visibilityChanged");
|
||||
window.dispatchEvent(event);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Sichtbarkeit der Kabelstrecken setzen
|
||||
handlePolylineCheckboxChange(checked);
|
||||
};
|
||||
|
||||
//---------------------------
|
||||
return (
|
||||
<div
|
||||
id="mainDataSheet"
|
||||
className="absolute top-3 right-3 w-1/6 min-w-[300px] max-w-[400px] z-10 bg-white p-2 rounded-lg shadow-lg"
|
||||
>
|
||||
<div className="flex flex-col gap-4 p-4">
|
||||
<div className="flex items-center justify-between space-x-2">
|
||||
<select
|
||||
onChange={handleAreaChange}
|
||||
id="stationListing"
|
||||
className="border-solid-1 p-2 rounded ml-1 font-semibold"
|
||||
style={{ minWidth: "150px", maxWidth: "200px" }}
|
||||
>
|
||||
<option value="Station wählen">Station wählen</option>
|
||||
{/*
|
||||
...new Map(
|
||||
(GisStationsStaticDistrict.Points || [])
|
||||
.filter(p => !!p.Area_Name)
|
||||
.map(p => [p.Area_Name, p])
|
||||
).values(),
|
||||
*/}
|
||||
{[
|
||||
...new Map(
|
||||
(GisStationsStaticDistrict.Points || [])
|
||||
.filter(p => !!p.Area_Name)
|
||||
.map(p => [p.Area_Name, p])
|
||||
).values(),
|
||||
].map(item => (
|
||||
<option key={item.Area_Name} value={item.IdLD}>
|
||||
{item.Area_Name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="flex items-center space-x-2">
|
||||
<EditModeToggle />
|
||||
<img
|
||||
src="/img/expand-icon.svg"
|
||||
alt="Expand"
|
||||
className="h-6 w-6 cursor-pointer"
|
||||
onClick={handleIconClick}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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 => (
|
||||
@@ -310,7 +301,7 @@ function MapLayersControlPanel({ handlePolylineCheckboxChange }) {
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={kabelstreckenVisible}
|
||||
onChange={e => handlePolylineCheckboxChange(e.target.checked)}
|
||||
onChange={e => onPolylineToggle(e.target.checked)}
|
||||
id="polyline-checkbox"
|
||||
disabled={!isTalasAllowed || editMode}
|
||||
/>
|
||||
|
||||
19
convert-md-to-confluence.ps1
Normal 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."
|
||||
BIN
docs/NodeMap.pdf
@@ -22,4 +22,73 @@ Verzeichnisstruktur funktioniert.
|
||||
|
||||
---
|
||||
|
||||
## OSM‑basierte, „open“ Quellen
|
||||
|
||||
Diese sind datenrechtlich offen (ODbL bzw. Community-Lizenzen), aber das „kostenlos“ gilt nicht im
|
||||
Sinne unbegrenzter Tile‑Nutzung. Die Tile‑Server werden als Community‑Ressource bereitgestellt –
|
||||
bitte Policies respektieren.
|
||||
|
||||
- osm-standard (OpenStreetMap)
|
||||
- - Key: Nein
|
||||
- - Nutzung: Fair‑Use; für produktive/hohe Last eigenen Tile‑Server/Provider verwenden.
|
||||
- - Attribution: „© OpenStreetMap contributors“
|
||||
|
||||
- osm-humanitarian (HOT)
|
||||
- - Key: Nein
|
||||
- - Nutzung: Fair‑Use; für größere Last die Betreiber kontaktieren bzw. andere Infrastruktur nutzen.
|
||||
- - Attribution: „© OpenStreetMap contributors <br>
|
||||
-
|
||||
- Humanitarian OpenStreetMap Team“ cyclosm
|
||||
- - Key: Nein
|
||||
- - Nutzung: Fair‑Use (bereitgestellt u. a. über OSM France). Für höhere Last
|
||||
Unterstützung/Hostingoptionen prüfen.
|
||||
- - Attribution: „CyclOSM“ + „OpenStreetMap contributors“
|
||||
- Praxis‑Tipps Kleine bis mittlere Nutzung: Die oben genannten „keyless“ Quellen sind oft
|
||||
ausreichend, solange du Attribution setzt und Limits respektierst. Produktion/hohe Last: Nutze
|
||||
einen Anbieter mit Vertrag/Key (z. B. Thunderforest, Tracestrack, MapTiler, Mapbox) oder hoste
|
||||
Tiles selbst. Schlüssel im Client: Für Thunderforest/Tracestrack stehen die Keys im Frontend. Das
|
||||
ist üblich, aber der Key ist sichtbar. Wenn du ihn verbergen willst, richte einen kleinen
|
||||
Tile‑Proxy auf deinem Server ein, der den Key serverseitig anhängt und optional cached.
|
||||
Attribution: Dein BaseMapPanel setzt bereits Attributionsstrings aus config.json. Achte darauf,
|
||||
dass sie je Quelle korrekt sind.
|
||||
|
||||
---
|
||||
|
||||
Kurzantwort: Für kommerzielle Nutzung sind OSM‑Community‑Tile‑Server nicht geeignet. Nutze einen
|
||||
bezahlten Anbieter (z. B. Thunderforest, Tracestrack) oder hoste selbst. Attribution ist immer
|
||||
Pflicht.
|
||||
|
||||
Links und Hinweise je Layer/Provider:
|
||||
|
||||
OpenStreetMap Standard (osm-standard)
|
||||
|
||||
Lizenz/Daten: ODbL, Attribution Pflicht Tile-Server-Policy (keine Produktion/hohe Last):
|
||||
https://operations.osmfoundation.org/policies/tiles/ Urheberrecht/Attribution:
|
||||
https://www.openstreetmap.org/copyright HOT Humanitarian (osm-humanitarian)
|
||||
|
||||
Community-Server (OSM France); keine Produktion/hohe Last Info/Policy OSM France Tiles:
|
||||
https://tile.openstreetmap.fr/ HOT: https://www.hotosm.org/ CyclOSM (cyclosm)
|
||||
|
||||
Community-Server (OSM France); keine Produktion/hohe Last Projektseite: https://www.cyclosm.org/
|
||||
Hinweise/Policy (OSM France): https://tile.openstreetmap.fr/ Wiki:
|
||||
https://wiki.openstreetmap.org/wiki/CyclOSM Carto Light (carto-light / Positron)
|
||||
|
||||
Keylos nutzbar mit Attribution; Fair‑Use, für hohe Last über CARTO‑Pläne Basemaps:
|
||||
https://carto.com/basemaps/ Attribution: https://carto.com/attributions Pricing (Plattform):
|
||||
https://carto.com/pricing/ (bei großem Volumen Sales kontaktieren) Thunderforest (Cycle/Transport u.
|
||||
a.)
|
||||
|
||||
Kommerziell mit API‑Key; Pläne von Free bis Pro Pricing: https://www.thunderforest.com/pricing/
|
||||
Terms/Attribution: https://www.thunderforest.com/terms/ Tracestrack Topo
|
||||
|
||||
API‑Key erforderlich; kostenlose und bezahlte Pläne Übersicht/Pricing:
|
||||
https://www.tracestrack.com/en/maps/ Nutzungsbedingungen: https://www.tracestrack.com/en/terms/
|
||||
Empfehlung:
|
||||
|
||||
Produktion: Nimm Thunderforest oder Tracestrack (oder MapTiler: https://www.maptiler.com/pricing/)
|
||||
oder hoste Tiles selbst. Attribution in der Karte anzeigen (OSM + jeweiliger Anbieter). Soll ich
|
||||
diese Links samt klarer Hinweise kompakt in eure README.md einpflegen?
|
||||
|
||||
---
|
||||
|
||||
[Zurück zur Übersicht](../README.md)
|
||||
|
||||
30
fix-confluence-headings.ps1
Normal 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."
|
||||
26
package-lock.json
generated
@@ -1,16 +1,17 @@
|
||||
{
|
||||
"name": "nodemap",
|
||||
"version": "1.1.350",
|
||||
"version": "1.1.396",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "nodemap",
|
||||
"version": "1.1.350",
|
||||
"version": "1.1.396",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@heroicons/react": "^2.1.5",
|
||||
"@iconify/react": "^6.0.1",
|
||||
"@mui/icons-material": "^6.0.2",
|
||||
"@reduxjs/toolkit": "^2.5.1",
|
||||
"autoprefixer": "^10.4.19",
|
||||
@@ -357,6 +358,27 @@
|
||||
"react": ">= 16 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/@iconify/react": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@iconify/react/-/react-6.0.1.tgz",
|
||||
"integrity": "sha512-fCocnAfiGXjrA0u7KkS3W/OQHNp9LRFICudvOtxmS3Mf7U92aDhP50wyzRbobZli51zYt9ksZ9g0J7H586XvOQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@iconify/types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/cyberalien"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@iconify/types": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
|
||||
"integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "nodemap",
|
||||
"version": "1.1.350",
|
||||
"version": "1.1.396",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@heroicons/react": "^2.1.5",
|
||||
"@iconify/react": "^6.0.1",
|
||||
"@mui/icons-material": "^6.0.2",
|
||||
"@reduxjs/toolkit": "^2.5.1",
|
||||
"autoprefixer": "^10.4.19",
|
||||
@@ -44,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"
|
||||
},
|
||||
|
||||
18
pages/api/testDbConnection.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// pages/api/testDbConnection.js
|
||||
import getPool from "../../utils/mysqlPool";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const pool = getPool();
|
||||
let connection;
|
||||
try {
|
||||
connection = await pool.getConnection();
|
||||
const [rows] = await connection.query("SELECT 1 AS test");
|
||||
console.log("DB-Verbindung erfolgreich! Ergebnis:", rows);
|
||||
res.status(200).json({ success: true, result: rows });
|
||||
} catch (error) {
|
||||
console.error("DB-Verbindungsfehler:", error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
} finally {
|
||||
if (connection) connection.release();
|
||||
}
|
||||
}
|
||||
35
playwright.config.js
Normal file
@@ -0,0 +1,35 @@
|
||||
// Playwright test configuration for the NodeMap project
|
||||
// Starts the local Next.js custom server (server.js) and runs tests against http://localhost:3000
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { defineConfig, devices } = require("@playwright/test");
|
||||
|
||||
module.exports = defineConfig({
|
||||
testDir: "./playwright/tests",
|
||||
timeout: 60_000,
|
||||
expect: { timeout: 10_000 },
|
||||
fullyParallel: true,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
// Reporters: keep console-friendly list and generate an HTML report under playwright/reports
|
||||
reporter: [["list"], ["html", { outputFolder: "playwright/reports", open: "never" }]],
|
||||
// Store any runner outputs (attachments, logs) under playwright/test-results
|
||||
outputDir: "playwright/test-results",
|
||||
use: {
|
||||
baseURL: "http://localhost:3000",
|
||||
// Disable artifact generation locally to avoid creating files
|
||||
trace: "off",
|
||||
video: "off",
|
||||
screenshot: "off",
|
||||
headless: process.env.PW_HEADED ? false : true,
|
||||
// Apply slow motion to all actions when PW_SLOWMO is set
|
||||
launchOptions: {
|
||||
slowMo: process.env.PW_SLOWMO ? Number(process.env.PW_SLOWMO) : 0,
|
||||
},
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
// example.spec.js
|
||||
const { test, expect } = require("@playwright/test");
|
||||
|
||||
test("simple test", async ({ page }) => {
|
||||
await page.goto("https://playwright.dev");
|
||||
await expect(page).toHaveTitle(/Playwright/);
|
||||
});
|
||||
244
playwright/tests/mapcomponent.spec.js
Normal 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
|
||||
*/
|
||||
@@ -5,22 +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
|
||||
}
|
||||
},
|
||||
"active": "osm",
|
||||
"_comment_active": "Aktive Kartenquelle: 'local' oder 'osm'",
|
||||
|
||||
"center": [53.111111, 8.4625],
|
||||
"_comment_center": "Startmittelpunkt der Karte (lat, lng)",
|
||||
|
||||
@@ -29,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 "
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 349 B |
3
public/img/icons/material-symbols/alarm.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 396 B |
3
public/img/icons/material-symbols/edit-off.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L18 21l-3-3m-12.728-.364A9 9 0 015.636 5.636m0 0L3 3l3 3m9.364 9.364L18 21M5.636 5.636L3 3" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 351 B |
3
public/img/icons/material-symbols/edit.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 432 B |
3
public/img/icons/material-symbols/info.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 333 B |
4
public/img/icons/material-symbols/map-marker.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="rgb(0, 174, 239)" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25s-7.5-4.108-7.5-11.25a7.5 7.5 0 1115 0z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 373 B |
3
public/img/icons/material-symbols/menu.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 231 B |
3
public/img/icons/material-symbols/search.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="rgb(0, 174, 239)">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 261 B |
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuites name="jest tests" tests="0" failures="0" errors="0" time="4.822">
|
||||
</testsuites>
|
||||
@@ -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 & Übersicht</h3>
|
||||
<ul>
|
||||
<li><a href="#projektüberblick">Projektüberblick</a></li>
|
||||
<li><a href="build-and-deploy.md">Build & 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">"//info"</span><span class="fu">:</span> <span class="st">"tileSources: 'local' für offline, 'osm' für online"</span><span class="fu">,</span></span>
|
||||
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">"tileSources"</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">"local"</span><span class="fu">:</span> <span class="st">"http://localhost/talas5/TileMap/mapTiles/{z}/{x}/{y}.png"</span><span class="fu">,</span></span>
|
||||
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">"osm"</span><span class="fu">:</span> <span class="st">"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"</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">"active"</span><span class="fu">:</span> <span class="st">"osm"</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 & 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 & Tools</a><br />
|
||||
➡ <a href="guide/env.md">Umgebungsvariablen (env-Dateien)</a></p>
|
||||
<hr />
|
||||
<h2 id="webservices-api-fluss">Webservices & 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 & 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 & Entwicklung</a></p>
|
||||
<hr />
|
||||
<h2 id="fehlerbehandlung-glossar">Fehlerbehandlung & 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 & 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 & Setup</strong> und folge dann über die
|
||||
Webservices bis zu den Komponenten.</p>
|
||||
@@ -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">"TALAS"</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">"click"</span><span class="op">,</span> () <span class="kw">=></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] --> B1[useDynamicDeviceLayers]
|
||||
B1 --> C1[loop über GisSystemStatic]
|
||||
C1 --> D1[createAndSetDevices]
|
||||
D1 --> E1[Filter stations aus Redux]
|
||||
E1 --> F1[erstelle Marker /Leaflet]
|
||||
F1 --> G1[Marker in LayerGroup einfügen]
|
||||
G1 --> H1[setMarkerStates im Hook]
|
||||
H1 --> A2[markerStates zurück nach MapComponent]
|
||||
|
||||
A2 --> I1[Map aktualisiert Marker]
|
||||
A2 --> 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>
|
||||
@@ -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>
|
||||
@@ -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 & 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 & 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->>TALASweb: mapTypC.aspx?m=12&u=484
|
||||
TALASweb-->>Browser: iFrame lädt NodeMap
|
||||
Browser->>NodeMap: Liest m & u aus URL
|
||||
NodeMap->>TALASweb: WebService-Requests (5 APIs)
|
||||
NodeMap->>MySQL: API-Anfragen zu POIs, Geräten, Linien
|
||||
NodeMap-->>Browser: Interaktive Karte anzeigen
|
||||
</code></pre>
|
||||
<hr />
|
||||
<h2 id="architekturüberblick">🗺️ Architekturüberblick</h2>
|
||||
<pre><code>+------------------+ +------------------+ +------------------+
|
||||
| Leaflet Map | <---> | Redux Store | <---> | Webservices |
|
||||
| (Interaktivität) | | (Status & Data) | | (IIS, .asmx) |
|
||||
+------------------+ +------------------+ +------------------+
|
||||
^
|
||||
|
|
||||
v
|
||||
+------------------+ +------------------+ +-------------------+
|
||||
| POI-Komponenten | <---> | Redux Slices | <---> | 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 &
|
||||
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 & DB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>POIs</td>
|
||||
<td><code>AddPOIModal.js</code>, <code>PoiUpdateModal.js</code></td>
|
||||
<td>UI für POI-Erstellung & -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 &
|
||||
Interaktion.</p></li>
|
||||
<li><p><strong>Kontextmenü-Logik:</strong><br />
|
||||
Marker & Polylinien besitzen eigene Kontextmenüs – dynamisch
|
||||
zusammengesetzt und verwaltet.</p></li>
|
||||
</ul>
|
||||
<hr />
|
||||
<h2 id="versionierung-builds">📦 Versionierung & 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["gisSystemStaticSlice → selectGisSystemStatic"]
|
||||
C2["gisStationsStaticDistrictSlice → selectGisStationsStaticDistrict"]
|
||||
C3["mapLayersSlice → mapLayersVisibility"]
|
||||
end
|
||||
|
||||
%% React
|
||||
subgraph React-Komponente
|
||||
D1[MapComponent.js]
|
||||
D2["useEffect: dynamische Layer"]
|
||||
D3["createAndSetDevices"]
|
||||
D4["layerRefs (useRef)"]
|
||||
D5["markerStates (useState)"]
|
||||
D6["useEffect: Sichtbarkeit prüfen"]
|
||||
D7["Map aktualisieren / add/remove"]
|
||||
D8["checkOverlappingMarkers"]
|
||||
end
|
||||
|
||||
%% Datenfluss
|
||||
A1 --> B1 --> C1 --> D2
|
||||
A2 --> B2 --> C2 --> D3
|
||||
C3 --> D6
|
||||
D2 --> D3
|
||||
D3 --> D4
|
||||
D3 --> D5
|
||||
D5 --> D6
|
||||
D6 --> D7
|
||||
D6 --> D8
|
||||
D7 --> D1
|
||||
|
||||
|
||||
|
||||
</code></pre>
|
||||
<hr />
|
||||
<p>Jetzt (dynamisch & 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) => ({ …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] --> B1[useDynamicDeviceLayers]
|
||||
B1 --> C1[loop über GisSystemStatic]
|
||||
C1 --> D1[createAndSetDevices]
|
||||
D1 --> E1[Filter stations aus Redux]
|
||||
E1 --> F1[erstelle Marker /Leaflet]
|
||||
F1 --> G1[Marker in LayerGroup einfügen]
|
||||
G1 --> H1[setMarkerStates im Hook]
|
||||
H1 --> A2[markerStates zurück nach MapComponent]
|
||||
|
||||
A2 --> I1[Map aktualisiert Marker]
|
||||
A2 --> 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->>WebService: fetch endpointX
|
||||
alt Daten haben sich geändert
|
||||
WS_Server-->>Browser: emit 'endpointXUpdated'
|
||||
Browser->>ReduxStore: dispatch(fetchEndpointXThunk())
|
||||
ReduxStore->>WebService: fetch endpointX
|
||||
WebService-->>ReduxStore: JSON response
|
||||
ReduxStore-->>UI: update Slice
|
||||
UI-->>User: re-render Markers
|
||||
else Keine Änderung
|
||||
WS_Server-->>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 & 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>
|
||||
@@ -0,0 +1,19 @@
|
||||
<h1 id="architektur-verbindung-gis-system-gis-station">📡 Architektur:
|
||||
Verbindung GIS-System & 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] --> B[useDynamicDeviceLayers.js]
|
||||
B --> C[Filter und Gruppierung nach System ID]
|
||||
C --> D[createAndSetDevices.js erzeugt Marker]
|
||||
D --> E[MapComponent.js zeigt Marker auf Karte]
|
||||
subgraph Redux
|
||||
F[fetchGisSystemStaticService.js liefert Systeme mit IdSystem]
|
||||
F --> G[fetchGisSystemStaticThunk.js]
|
||||
G --> H[setInitialLayers mit system-IdSystem]
|
||||
H --> I[mapLayersSlice.js speichert Sichtbarkeit]
|
||||
end
|
||||
I -->|Sichtbarkeit steuert Anzeige| E</code></pre>
|
||||
<hr />
|
||||
<p><a href="../README.md">Zurück zur Übersicht</a></p>
|
||||
@@ -0,0 +1,100 @@
|
||||
<!-- /docs/build-amddeploy.md -->
|
||||
<h1 id="deployment-build-verhalten-next.js">🛠 Deployment &
|
||||
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>
|
||||
@@ -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 & 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>
|
||||
@@ -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 & 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 &
|
||||
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 & 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 & testbar aufgebaut</li>
|
||||
</ul>
|
||||
@@ -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 'Stützpunkt entfernen' 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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 --> S1
|
||||
T2 --> S2
|
||||
T3 --> S3
|
||||
|
||||
T1 --> A1
|
||||
T2 --> A2
|
||||
T3 --> A3
|
||||
|
||||
A1 --> C1
|
||||
A2 --> C1
|
||||
A3 --> C1
|
||||
|
||||
C1 --> C2
|
||||
C2 --> C4
|
||||
C1 --> 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 & 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>
|
||||
@@ -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×10 px, IconSize 25×25 px (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>
|
||||
@@ -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×14 px</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>
|
||||
@@ -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>
|
||||
@@ -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×18 px</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>
|
||||
@@ -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×24 px</li>
|
||||
</ul>
|
||||
<h2 id="removesupportpointicon">RemoveSupportPointIcon</h2>
|
||||
<ul>
|
||||
<li>Roter Kreis mit weißem Rand und Minuszeichen</li>
|
||||
<li><code>iconSize</code>: 24×24 px</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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 & 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 & 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 & 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 & 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 & Center dauerhaft im Browser</p></li>
|
||||
<li><p>Kontextmenü-Einträge ändern sich je nach Rechten & 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 &
|
||||
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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,5 @@
|
||||
<h1 id="übersicht-docscomponentsmaincomponenthooks">📄 Übersicht:
|
||||
docs/components/mainComponent/hooks</h1>
|
||||
<ul>
|
||||
<li><a href="useInitializeMap.md">useInitializeMap</a></li>
|
||||
</ul>
|
||||
@@ -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><div id="map"></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">=></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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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">""</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"><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">=></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">","</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">/></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>
|
||||
@@ -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>
|
||||
@@ -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 & 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>
|
||||
@@ -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">=></span> localStorage<span class="op">.</span><span class="fu">getItem</span>(<span class="st">"editMode"</span>) <span class="op">===</span> <span class="st">"true"</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>
|
||||
@@ -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 & 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>
|
||||
@@ -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>
|
||||
@@ -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">OSM‑basierte, „open“ Quellen</h2>
|
||||
<p>Diese sind datenrechtlich offen (ODbL bzw. Community-Lizenzen), aber
|
||||
das „kostenlos“ gilt nicht im Sinne unbegrenzter Tile‑Nutzung. Die
|
||||
Tile‑Server werden als Community‑Ressource bereitgestellt – bitte
|
||||
Policies respektieren.</p>
|
||||
<ul>
|
||||
<li>osm-standard (OpenStreetMap)</li>
|
||||
<li><ul>
|
||||
<li>Key: Nein</li>
|
||||
</ul></li>
|
||||
<li><ul>
|
||||
<li>Nutzung: Fair‑Use; für produktive/hohe Last eigenen
|
||||
Tile‑Server/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: Fair‑Use; 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: Fair‑Use (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>Praxis‑Tipps Kleine bis mittlere Nutzung: Die oben genannten
|
||||
„keyless“ Quellen sind oft ausreichend, solange du Attribution setzt und
|
||||
Limits respektierst. Produktion/hohe Last: Nutze einen Anbieter mit
|
||||
Vertrag/Key (z. B. Thunderforest, Tracestrack, MapTiler, Mapbox) oder
|
||||
hoste Tiles selbst. Schlüssel im Client: Für Thunderforest/Tracestrack
|
||||
stehen die Keys im Frontend. Das ist üblich, aber der Key ist sichtbar.
|
||||
Wenn du ihn verbergen willst, richte einen kleinen Tile‑Proxy auf deinem
|
||||
Server ein, der den Key serverseitig anhängt und optional cached.
|
||||
Attribution: Dein BaseMapPanel setzt bereits Attributionsstrings aus
|
||||
config.json. Achte darauf, dass sie je Quelle korrekt sind.</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<p>Kurzantwort: Für kommerzielle Nutzung sind OSM‑Community‑Tile‑Server
|
||||
nicht geeignet. Nutze einen bezahlten Anbieter (z. B. Thunderforest,
|
||||
Tracestrack) oder hoste selbst. Attribution ist immer Pflicht.</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; Fair‑Use, für hohe Last über
|
||||
CARTO‑Pläne Basemaps: https://carto.com/basemaps/ Attribution:
|
||||
https://carto.com/attributions Pricing (Plattform):
|
||||
https://carto.com/pricing/ (bei großem Volumen Sales kontaktieren)
|
||||
Thunderforest (Cycle/Transport u. a.)</p>
|
||||
<p>Kommerziell mit API‑Key; Pläne von Free bis Pro Pricing:
|
||||
https://www.thunderforest.com/pricing/ Terms/Attribution:
|
||||
https://www.thunderforest.com/terms/ Tracestrack Topo</p>
|
||||
<p>API‑Key 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>
|
||||
@@ -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">""</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">""</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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 & Tooling</h2>
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width: 32%" />
|
||||
<col style="width: 67%" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Paket</th>
|
||||
<th>Zweck & 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 & Karten</h2>
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width: 40%" />
|
||||
<col style="width: 59%" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Paket</th>
|
||||
<th>Zweck & 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 & Redux</h2>
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width: 33%" />
|
||||
<col style="width: 66%" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Paket</th>
|
||||
<th>Zweck & 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 & 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 & Styling</h2>
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width: 43%" />
|
||||
<col style="width: 56%" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Paket</th>
|
||||
<th>Zweck & 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 & Kommunikation</h2>
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width: 35%" />
|
||||
<col style="width: 64%" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Paket</th>
|
||||
<th>Zweck & 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 & 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 & Netzwerk</h2>
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width: 30%" />
|
||||
<col style="width: 69%" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Paket</th>
|
||||
<th>Zweck & Beschreibung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>http-proxy-middleware</strong></td>
|
||||
<td>API-Routing & 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 & Test
|
||||
(nur devDependencies)</h2>
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style="width: 60%" />
|
||||
<col style="width: 39%" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Paket</th>
|
||||
<th>Zweck & 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- & 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 & Hilfen</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Paket</th>
|
||||
<th>Zweck & 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>
|
||||
@@ -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> & <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
|
||||
{
|
||||
...
|
||||
"basePath": "/talas5"
|
||||
}
|
||||
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
|
||||
{
|
||||
...
|
||||
"basePath": "/talas5"
|
||||
}
|
||||
NEXT_PUBLIC_DEBUG=true</code></pre>
|
||||
<hr />
|
||||
<p><a href="../README.md">Zurück zur Übersicht</a></p>
|
||||
@@ -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&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://<ip>/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>
|
||||
@@ -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>
|
||||
@@ -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 & 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">"/api/mocks/webservice/GisSystemStatic"</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 &
|
||||
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- & 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>
|
||||
@@ -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 & 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 & 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 & 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>
|
||||
@@ -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">&</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>
|
||||
@@ -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-<IdSystem></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>
|
||||
@@ -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 & 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>
|
||||
@@ -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 & 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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||