From 463ab97c4b8536bf72b1fac0d416d11e24a5a9df Mon Sep 17 00:00:00 2001 From: fixclean Date: Wed, 18 Feb 2026 09:05:16 +0100 Subject: [PATCH 1/9] todos check --- TODO.md | 293 +++++++------------------------------------------------- 1 file changed, 37 insertions(+), 256 deletions(-) diff --git a/TODO.md b/TODO.md index 8119d0d..8023301 100644 --- a/TODO.md +++ b/TODO.md @@ -1,258 +1,39 @@ # TODO -Zentrale Sammlung aller TODO-Markierungen im Repository (Stand: 13. Februar 2026). - -Hinweis: Die Eintraege sind direkt aus den Quelldateien aggregiert. - -## Arbeitsnotizen (16. Februar 2026) - -- [x] API-Basis umgesetzt: `app/api/connections.php`, `app/api/device_type_ports.php`, `app/api/upload.php` auf aktuelles Schema gebracht (Auth, Validierung, Existenzpruefungen, Fehlerantworten). -- [x] Bootstrap/Auth/Config/Routing umgesetzt: `app/config.php`, `app/bootstrap.php`, `app/lib/_sql.php`, `app/lib/auth.php`, `app/index.php`. -- [x] Frontend-Grundlagen aktualisiert: `app/assets/js/app.js`, `app/assets/js/dashboard.js`, `app/assets/js/svg-editor.js`, `app/assets/js/network-view.js`. -- [x] Delete-Flow fuer zentrale Module umgesetzt: `buildings`, `floors`, `racks`, `device_types`, `floor_infrastructure`. -- [x] Legacy-Mock ersetzt: `app/modules/device_types/ports.php` lauffaehig gemacht (anzeigen, hinzufuegen, loeschen). -- [x] TODO-Reste in `header.php`, `footer.php`, `layout.php`, `floor_infrastructure/edit.php` entfernt. - -Offene Blocker / naechste Punkte: -- [ ] `app/modules/connections/list.php`: Detailbereich fuer ausgewaehlte Verbindung sowie Bearbeiten/Loeschen im UI fehlen noch. -- [ ] `app/modules/dashboard/list.php`: grosse zoombare Gesamt-Topologie-Wand (fachlich/grafisch groesseres Feature). -- [ ] `app/lib/helpers.php`: generischer Sammel-TODO ohne konkreten Scope. -- [ ] Vollstaendiger End-to-End-Test aktuell nicht moeglich, da in dieser Shell kein `php` CLI verfuegbar ist. - -## app\api\connections.php - -- [ ] L15: // TODO: Single-User-Auth prüfen -- [ ] L61: // TODO: Kontext definieren (Standort, Rack, Floor, gesamtes Netz) -- [ ] L117: // TODO: Validierung -- [ ] L182: // TODO: Prüfen, ob Verbindung existiert - -## app\api\device_type_ports.php - -- [ ] L15: // TODO: Single-User-Auth prüfen -- [ ] L96: // TODO: Transaktion starten (falls SQL-Klasse das unterstützt) -- [ ] L100: // TODO: Validierung: -- [ ] L163: // TODO: Prüfen, ob Port existiert und nicht verwendet wird - -## app\api\upload.php - -- [ ] L18: // TODO: Single-User-Auth prüfen -- [ ] L25: // TODO: Upload-Basisverzeichnis aus config.php -- [ ] L35: // TODO: Max. Dateigröße festlegen (z.B. 5MB) -- [ ] L77: // TODO: Kategorie definieren (device_types, floors, racks, etc.) -- [ ] L95: // TODO: Eindeutigen Namen besser definieren (UUID?) -- [ ] L114: // TODO: Optional in Tabelle `uploads` speichern - -## app\assets\js\app.js - -- [ ] L15: deviceTypes: [], // TODO: alle Gerätetypen laden -- [ ] L16: devices: [], // TODO: alle Geräte laden -- [ ] L17: racks: [], // TODO: alle Racks laden -- [ ] L18: floors: [], // TODO: alle Floors laden -- [ ] L19: connections: [], // TODO: alle Verbindungen laden -- [ ] L33: // TODO: import / init svg-editor.js -- [ ] L39: // TODO: import / init network-view.js -- [ ] L45: // TODO: init drag & drop logic -- [ ] L59: // TODO: Save-Button Device-Type -- [ ] L64: // TODO: Save Device-Type via AJAX -- [ ] L68: // TODO: Save-Button Device -- [ ] L73: // TODO: Save Device via AJAX -- [ ] L77: // TODO: Save-Button Floor -- [ ] L82: // TODO: Save Floor via AJAX -- [ ] L86: // TODO: Save-Button Rack -- [ ] L91: // TODO: Save Rack via AJAX -- [ ] L95: // TODO: Weitere Event-Handler (Import, Export, Filter) -- [ ] L132: // TODO: weitere Utility-Funktionen (DOM-Helper, SVG-Helper, etc.) - -## app\assets\js\dashboard.js - -- [ ] L75: // TODO: Dashboard-Container ermitteln -- [ ] L78: // TODO: Module rendern -- [ ] L81: // TODO: Optional: Status-Daten laden (Counts, Warnings) -- [ ] L117: // TODO: loadStats() → Anzahl Geräte, offene Ports, unverbundene Dosen -- [ ] L118: // TODO: showWarnings() → unverbundene Ports, VLAN-Konflikte -- [ ] L119: // TODO: RecentChanges() → letzte Änderungen - -## app\assets\js\network-view.js - -- [ ] L20: // TODO: Standort / Rack / View-Kontext vom Backend setzen -- [ ] L23: // TODO: API-Endpunkte definieren -- [ ] L79: // TODO: Datenstruktur validieren -- [ ] L123: // TODO: Gerätetyp (SVG oder JPG) korrekt laden -- [ ] L144: // TODO: Ports als kleine Kreise anlegen (Position aus Portdefinition) -- [ ] L145: // TODO: Ports klickbar machen (für Verbindungs-Erstellung) -- [ ] L157: // TODO: Quell- & Ziel-Port-Koordinaten berechnen -- [ ] L158: // TODO: unterschiedliche Verbindungstypen (Farbe, Strichart, Dicke) -- [ ] L217: // TODO: Positionen optional automatisch speichern -- [ ] L232: // TODO: Sidebar mit Gerätedetails füllen -- [ ] L254: // TODO: Erfolg / Fehler anzeigen -- [ ] L289: // TODO: Delete -> Gerät entfernen? - -## app\assets\js\svg-editor.js - -- [ ] L20: // TODO: vom Backend setzen (z. B. via data-Attribut) -- [ ] L23: // TODO: API-Endpunkte festlegen -- [ ] L74: // TODO: Modifier-Key prüfen (z. B. nur mit SHIFT neuen Port erstellen?) -- [ ] L86: port_type_id: null, // TODO: Default-Porttyp? -- [ ] L134: // TODO: Sidebar-Felder mit Portdaten füllen -- [ ] L178: // TODO: Sicherheitsabfrage (confirm) -- [ ] L184: // TODO: Sidebar zurücksetzen -- [ ] L200: // TODO: Datenformat validieren -- [ ] L222: // TODO: Erfolg / Fehler anzeigen - -## app\bootstrap.php - -- [ ] L16: // TODO: Config-Datei mit DB-Zugang, Pfaden, globalen Settings füllen -- [ ] L22: // TODO: Single-User Auth prüfen -- [ ] L30: // TODO: Host, User, Passwort, DB aus config.php nutzen - -## app\index.php - -- [ ] L19: // TODO: Session starten / Single-User-Auth prüfen -- [ ] L37: // TODO: Fehlerseite anzeigen, nutze renderClientError(...) -- [ ] L42: // TODO: Fehlerseite anzeigen, nutze renderClientError(...) -- [ ] L61: // TODO: Fehlerseite oder 404, nutze renderClientError(...) - -## app\lib\auth.php - -- [ ] L24: // TODO: Session-Variable definieren, z.B. $_SESSION['auth'] === true -- [ ] L40: // TODO: Passwort aus config.php vergleichen -- [ ] L41: // TODO: Passwort-Hash verwenden (password_hash / password_verify) -- [ ] L62: // TODO: Session-Variablen löschen -- [ ] L65: // TODO: Optional komplette Session zerstören -- [ ] L79: // TODO: Redirect auf Login-Seite - -## app\lib\helpers.php - -- [ ] L300: // TODO: Weitere Helfer nach Bedarf - -## app\modules\buildings\edit.php - -- [ ] L176: // TODO: AJAX-Delete implementieren - -## app\modules\buildings\list.php - -- [ ] L245: // TODO: AJAX-Delete implementieren - -## app\modules\connections\list.php - -- [ ] L198: -- [ ] L207: - -## app\modules\device_types\edit.php - -- [ ] L18: //TODO port hinzufügen geht nicht -- [ ] L378: // TODO: AJAX-Delete implementieren - -## app\modules\device_types\list.php - -- [ ] L252: // TODO: AJAX-Delete implementieren - -## app\modules\device_types\ports.php - -- [ ] L12: // TODO: bootstrap laden -- [ ] L15: // TODO: Auth erzwingen -- [ ] L22: // TODO: device_type_id aus GET lesen -- [ ] L25: // TODO: Gerätetyp laden -- [ ] L28: // TODO: Ports dieses Gerätetyps laden -- [ ] L43: -- [ ] L59: -- [ ] L60: -- [ ] L109: -- [ ] L113: -- [ ] L117: -- [ ] L120: -- [ ] L123: -- [ ] L126: -- [ ] L161: -- [ ] L262: * TODO: Replace this mock logic with real AJAX once ports are - -## app\modules\devices\list.php - -- [ ] L206: //TODO löschen geht nicht - -## app\modules\floor_infrastructure\edit.php - -- [ ] L277: //TODO drag an drop auf der stockwerkskarte für die patchfelder und wandbuchsen. buchsen haben eine einheitliche größe, und sind quadratisch, patchfelder sind auch für sich einheitlich, sind rechteckig und breiter als hoch -- [ ] L278: //TODO style in css files einsortieren - -## app\modules\floor_infrastructure\list.php - -- [ ] L143:

//TODO: SVG-Editor mit Drag & Drop für diese Objekte erweitern (siehe Stockwerke-Modul).

- -## app\modules\floors\list.php - -- [ ] L237: // TODO: AJAX-Delete implementieren - -## app\modules\locations\edit.php - -- [ ] L157: // TODO: AJAX-Delete implementieren - -## app\modules\locations\list.php - -- [ ] L134: //TODO design schlecht, mach es hübscher -- [ ] L208: //TODO style in css file - -## app\modules\racks\edit.php - -- [ ] L201: // TODO: AJAX-Delete implementieren -- [ ] L221: -- [ ] L237: -- [ ] L251: // TODO: Rack-ID aus PHP setzen -- [ ] L254: // TODO: Gerätepositionen an JS übergeben - -## app\modules\racks\list.php - -- [ ] L255: // TODO: AJAX-Delete implementieren - -## app\templates\footer.php - -- [ ] L14: -- [ ] L17: - -## app\templates\header.php - -- [ ] L24: - -## app\templates\layout.php - -- [ ] L11: * TODO: In Zukunft: zentrales Template-System (z.B. mit $content) -- [ ] L18: - -## BUGS.md - -- [ ] L3: - [ ] TODO Design vereinheitlichen - -## doc\DATABASE.md - -- [ ] L126: **TODO** - -## IMPLEMENTATION_STATUS.md - -- [ ] L80: - [ ] **Delete-Funktionen** - Löschen noch als TODO (als AJAX implementieren) -- [ ] L109: │ └── auth.php 🚧 TODO: Auth - -## init.sql - -- [ ] L372: -- TODO: Port-Konfiguration (Patchpanel ↔ Patchpanel, Patchpanel ↔ Netzwerkbuchse) wird über die `connections`-Tabelle geregelt. - -## NEXT_STEPS.md - -- [ ] L74: ## 🔧 Bekannte TODOs im Code -- [ ] L76: Alle noch offenen Punkte sind mit `// TODO:` gekennzeichnet: -- [ ] L79: # Alle TODOs finden: -- [ ] L80: grep -r "TODO:" app/modules/ --include="*.php" -- [ ] L83: Wichtigste TODOs: - -## README.md - -- [ ] L241: ### TODO: Patchpanel-Infrastruktur -- [ ] L253: - TODO: SVG-Editor um Drag & Drop für diese Objekte erweitern und Klicks direkt mit dem Modul verbinden. - -- [ ] //TODO infrastruktur patchfelder löschen soll implementiert werden. - -## Topologie-Abgleich (16. Februar 2026) - -- [ ] #TODO: `connections.port_a_type` und `connections.port_b_type` um einen Patchpanel-Port-Typ erweitern (z. B. `patchpanel`) und auf `floor_patchpanel_ports.id` referenzieren. -- [ ] #TODO: Business-Regeln fuer Topologie in der Verbindungs-Validierung hinterlegen: Patchpanel-Port nur mit Patchpanel-Port oder Netzwerkbuchsen-Port verbinden. -- [ ] #TODO: Port-CRUD fuer Patchpanels ergaenzen: `floor_patchpanel_ports` beim Speichern aus `port_count` erzeugen/synchronisieren. -- [ ] #TODO: Port-CRUD fuer Netzwerkbuchsen ergaenzen: `network_outlet_ports` pflegen (mindestens ein Port je Buchse) und fuer Verbindungen nutzbar machen. +Bereinigte und aktuelle TODO-Liste (Stand: 18. Februar 2026). +Quelle: vorhandene `TODO`-Marker im Repository plus offene Architekturpunkte. + +## Erledigt (bereits umgesetzt) + +- [x] API-Basis umgesetzt (`app/api/connections.php`, `app/api/device_type_ports.php`, `app/api/upload.php`). +- [x] Bootstrap/Auth/Config/Routing-Grundlagen umgesetzt (`app/config.php`, `app/bootstrap.php`, `app/lib/_sql.php`, `app/lib/auth.php`, `app/index.php`). +- [x] Frontend-Grundlagen aktualisiert (`app/assets/js/app.js`, `app/assets/js/dashboard.js`, `app/assets/js/svg-editor.js`, `app/assets/js/network-view.js`). +- [x] Delete-Flow fuer zentrale Module umgesetzt (`buildings`, `floors`, `racks`, `device_types`, `floor_infrastructure`). +- [x] Legacy-Mock in `app/modules/device_types/ports.php` ersetzt. +- [x] Veraltete Sammel-TODO-Liste (nicht mehr im Code vorhanden) entfernt. + +## Offen (direkt im Code markiert) + +- [ ] `app/modules/dashboard/list.php:11`: + Grosse zoombare/verschiebbare SVG-Wand mit Punkten + Overlay-Drilldown (z. B. Rack-Ansicht). +- [ ] `app/modules/connections/list.php:387`: + Verbindung im UI bearbeiten/loeschen. +- [ ] `app/lib/helpers.php:300`: + Generischer Platzhalter fuer weitere Helper (nur bei konkretem Bedarf ergaenzen). + +## Offen (Bugs / Doku / Statusdateien) + +- [ ] `BUGS.md:3`: Design vereinheitlichen. +- [ ] `IMPLEMENTATION_STATUS.md:80`: Delete-Funktionen-Status aktualisieren. +- [ ] `IMPLEMENTATION_STATUS.md:109`: Auth-Status aktualisieren. +- [ ] `README.md:241`: Patchpanel-Infrastruktur umsetzen und danach Doku abhaken. +- [ ] `README.md:253`: SVG-Editor fuer Floor-Infrastruktur (Drag-and-Drop + direkte Modul-Interaktion). +- [ ] `doc/DATABASE.md:127`: TODO-Abschnitt fuer Patchpanel/Floorplan finalisieren. +- [ ] `init.sql:379`: Kommentar zur Port-Konfiguration in verbindliche Implementierungsregeln ueberfuehren. + +## Topologie-Backlog (ausstehend) + +- [ ] `connections.port_a_type` / `connections.port_b_type` um `patchpanel` erweitern und auf `floor_patchpanel_ports.id` referenzieren. +- [ ] Validierungsregeln fuer Topologie fest verdrahten (Patchpanel-Port nur mit Patchpanel-Port oder Netzwerkbuchsen-Port). +- [ ] Port-CRUD fuer Patchpanels: `floor_patchpanel_ports` aus `port_count` erzeugen/synchronisieren. +- [ ] Port-CRUD fuer Netzwerkbuchsen: `network_outlet_ports` pflegen (mindestens ein Port je Buchse) und in Verbindungen nutzbar machen. From c8fb5b140cfe8fe1718f07b5d93f538e16ce88d9 Mon Sep 17 00:00:00 2001 From: fixclean Date: Wed, 18 Feb 2026 09:23:11 +0100 Subject: [PATCH 2/9] feat: improve dashboard and connection workflows - add connection delete endpoint and update connection list handling - expand dashboard visualization behavior - update helpers/header and project TODO tracking --- NEXT_STEPS.md | 76 ++-- TODO.md | 11 +- app/lib/helpers.php | 85 +++- app/modules/connections/delete.php | 41 ++ app/modules/connections/list.php | 420 ++++++++++++-------- app/modules/dashboard/list.php | 600 +++++++++++++++++++++++++++-- app/templates/header.php | 1 + 7 files changed, 998 insertions(+), 236 deletions(-) create mode 100644 app/modules/connections/delete.php diff --git a/NEXT_STEPS.md b/NEXT_STEPS.md index 6648520..d4830a5 100644 --- a/NEXT_STEPS.md +++ b/NEXT_STEPS.md @@ -1,6 +1,6 @@ -# 📋 NÄCHSTE ARBEITSPAKETE +# 📋 NÄCHSTE ARBEITSPAKETE -## 🎯 Für die nächsten Sessions +## 🎯 Für die nächsten Sessions ### Package 1: Fehlerbehandlung & Sessions (1-2h) - [ ] Session-Handling in `bootstrap.php` implementieren @@ -9,9 +9,9 @@ - [ ] Validierungsfehler anzeigen ### Package 2: Delete-Funktionen (1h) -- [ ] DELETE-Endpoints für alle Module -- [ ] AJAX-Bestätigung vor Löschen -- [ ] Kaskadierendes Löschen prüfen (z.B. Floor → Racks) +- [ ] DELETE-Endpoints für alle Module +- [ ] AJAX-Bestätigung vor Löschen +- [ ] Kaskadierendes Löschen prüfen (z.B. Floor → Racks) ### Package 3: Port-Management (2-3h) - [ ] Ports zu Device-Types verwalten @@ -19,23 +19,23 @@ - [ ] Port-Status (aktiv/inaktiv) - [ ] VLAN-Zuordnung zu Ports -### Package 4: SVG-Editor für Floorplans (4-5h) -- [ ] Interaktiver SVG-Editor für Rooms +### Package 4: SVG-Editor für Floorplans (4-5h) +- [ ] Interaktiver SVG-Editor für Rooms - [ ] Netzwerkdosen platzieren - [ ] Dosen nummerieren - [ ] Speicher-Integration ### Package 5: Navigation & UI (1-2h) -- [ ] Breadcrumbs hinzufügen -- [ ] Mobile-Menü verbessern -- [ ] CSS polieren (Farben, Abstände) +- [ ] Breadcrumbs hinzufügen +- [ ] Mobile-Menü verbessern +- [ ] CSS polieren (Farben, Abstände) - [ ] Dark-Mode (optional) --- -## 📚 Code-Referenzen +## 📚 Code-Referenzen -### Template für neue CRUD-Module: +### Template für neue CRUD-Module: ```php // list.php: Filter + Tabelle // edit.php: Formular @@ -71,7 +71,7 @@ $whereSql = $where ? "WHERE " . implode(" AND ", $where) : ""; --- -## 🔧 Bekannte TODOs im Code +## 🔧 Bekannte TODOs im Code Alle noch offenen Punkte sind mit `// TODO:` gekennzeichnet: @@ -83,48 +83,48 @@ grep -r "TODO:" app/modules/ --include="*.php" Wichtigste TODOs: - `index.php:19` - Session starten - `*/save.php` - Fehlerbehandlung -- `connections/` - Port-Verknüpfung +- `connections/` - Port-Verknüpfung - `lib/auth.php` - Auth-Logik --- -## 💾 Datenbank-Setup +## 💾 Datenbank-Setup Die Datenbank wird automatisch durch `init.sql` initialisiert. Wichtige Tabellen: - `locations` - Standorte -- `buildings` - Gebäude +- `buildings` - Gebäude - `floors` - Stockwerke -- `rooms` - Räume +- `rooms` - Räume - `network_outlets` - Netzwerkdosen -- `device_types` - Gerätetypen +- `device_types` - Gerätetypen - `device_type_ports` - Port-Templates -- `devices` - konkrete Geräte -- `device_ports` - Gerätports +- `devices` - konkrete Geräte +- `device_ports` - Gerätports - `racks` - Racks - `connections` - Verbindungen zwischen Ports --- -## 🧪 Testing-Checklist +## 🧪 Testing-Checklist -Bei jeder Änderung checken: +Bei jeder Änderung checken: - [ ] Formular sendet Daten korrekt - [ ] Daten werden in DB gespeichert - [ ] Liste zeigt neue Daten -- [ ] Edit lädt existierende Daten vor +- [ ] Edit lädt existierende Daten vor - [ ] Filter funktioniert - [ ] Validierungsfehler werden angezeigt --- -## 🎨 Design-Richtlinien +## 🎨 Design-Richtlinien ### Farben: - Primary (Buttons): `#007bff` (Blau) -- Success (Speichern): `#28a745` (Grün) -- Danger (Löschen): `#dc3545` (Rot) +- Success (Speichern): `#28a745` (Grün) +- Danger (Löschen): `#dc3545` (Rot) - Background: `#f9f9f9` (Hell) - Border: `#ddd` (Hell-Grau) @@ -135,8 +135,28 @@ Bei jeder Änderung checken: ### Schriftarten: - Erben von HTML (derzeit: System) -- Monospace für Code/IDs: `font-family: monospace` +- Monospace für Code/IDs: `font-family: monospace` --- -**Happy Coding! 🚀** +**Happy Coding! 🚀** + +## Aktuell offene TODOs (Stand: 18. Februar 2026) + +- [ ] #15 Neue Verbindung: Es kann keine Netzwerkdose ausgewahlt werden. +- [ ] #14 Hilfslinien der Stockwerkskarten nur im Edit-Mode anzeigen; im Anzeige-Mode ausblenden. +- [ ] #11 Encoding- und Umlautfehler beheben (inkl. ae/oe/ue-Themen). +- [ ] #10 Dashboard-Grafik erzeugen: + - Oberste Ebene: Locations, ggf. mit Unterordnung. + - Darunter: Gebaudekomplexe mit Rack-Verbindungen. + - Darunter: Stockwerke. + - Darunter: Etagenweise Verbindungen. +- [ ] #8 Gerate löschen fehlt: Es erfolgt Weiterleitung, aber keine echte Fehlermeldung. +- [ ] #7 Letzten Punkt im Floor-Editor löschen: + - URL: `http://localhost/?module=floors&action=edit&id=1` +- [ ] #5 Dashboard als zoombare und verschiebbare SVG-Flache: + - Gerate anordnen. + - Gerate, Ports und Verbindungen anklickbar. + - Sprechblase mit Infos und Buttons zu Bereichen (editieren, entfernen, ...). +- [ ] #4 `device_types/edit`: Option "Ports automatisch erstellen" nur beim Erstellen anzeigen, nicht beim Editieren. + diff --git a/TODO.md b/TODO.md index 8023301..6cf7f5b 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,4 @@ -# TODO +# TODO Bereinigte und aktuelle TODO-Liste (Stand: 18. Februar 2026). Quelle: vorhandene `TODO`-Marker im Repository plus offene Architekturpunkte. @@ -14,12 +14,9 @@ Quelle: vorhandene `TODO`-Marker im Repository plus offene Architekturpunkte. ## Offen (direkt im Code markiert) -- [ ] `app/modules/dashboard/list.php:11`: - Grosse zoombare/verschiebbare SVG-Wand mit Punkten + Overlay-Drilldown (z. B. Rack-Ansicht). -- [ ] `app/modules/connections/list.php:387`: - Verbindung im UI bearbeiten/loeschen. -- [ ] `app/lib/helpers.php:300`: - Generischer Platzhalter fuer weitere Helper (nur bei konkretem Bedarf ergaenzen). +- [x] `app/modules/dashboard/list.php`: zoombare/verschiebbare SVG-Wand mit klickbaren Punkten und Overlay-Drilldown umgesetzt. +- [x] `app/modules/connections/list.php`: Detailbereich fuer ausgewaehlte Verbindung inkl. Bearbeiten/Loeschen im UI umgesetzt. +- [x] `app/lib/helpers.php`: konkrete allgemeine Helper ergaenzt (`formatDateTime`, `formatBytes`, `generateUuidV4`, `normalizeSvgCoordinate`). ## Offen (Bugs / Doku / Statusdateien) diff --git a/app/lib/helpers.php b/app/lib/helpers.php index 9746082..cd3d98c 100644 --- a/app/lib/helpers.php +++ b/app/lib/helpers.php @@ -297,8 +297,83 @@ HTML, [ * Sonstiges * ========================= */ -// TODO: Weitere Helfer nach Bedarf -// - Datum formatieren -// - Bytes → MB -// - UUID erzeugen -// - SVG-Koordinaten normalisieren +/** + * Formatiert Datum/Uhrzeit robust oder gibt Fallback zurueck. + * + * @param string|null $value + * @param string $format + * @param string $fallback + * @return string + */ +function formatDateTime(?string $value, string $format = 'd.m.Y H:i', string $fallback = '-'): string +{ + if ($value === null || trim($value) === '') { + return $fallback; + } + + $timestamp = strtotime($value); + if ($timestamp === false) { + return $fallback; + } + + return date($format, $timestamp); +} + +/** + * Formatiert Byte-Werte in menschenlesbare Einheit. + * + * @param int|float $bytes + * @param int $precision + * @return string + */ +function formatBytes($bytes, int $precision = 2): string +{ + $value = (float)$bytes; + if ($value <= 0) { + return '0 B'; + } + + $units = ['B', 'KB', 'MB', 'GB', 'TB']; + $power = min((int)floor(log($value, 1024)), count($units) - 1); + $scaled = $value / (1024 ** $power); + + return number_format($scaled, $precision, '.', '') . ' ' . $units[$power]; +} + +/** + * Erzeugt eine UUID v4. + * + * @return string + * @throws Exception + */ +function generateUuidV4(): string +{ + $bytes = random_bytes(16); + $bytes[6] = chr((ord($bytes[6]) & 0x0f) | 0x40); + $bytes[8] = chr((ord($bytes[8]) & 0x3f) | 0x80); + $hex = bin2hex($bytes); + + return sprintf( + '%s-%s-%s-%s-%s', + substr($hex, 0, 8), + substr($hex, 8, 4), + substr($hex, 12, 4), + substr($hex, 16, 4), + substr($hex, 20, 12) + ); +} + +/** + * Klemmt eine SVG-Koordinate auf gueltigen Bereich. + * + * @param float $value + * @param float $min + * @param float $max + * @param int $precision + * @return float + */ +function normalizeSvgCoordinate(float $value, float $min, float $max, int $precision = 2): float +{ + $normalized = max($min, min($max, $value)); + return round($normalized, $precision); +} diff --git a/app/modules/connections/delete.php b/app/modules/connections/delete.php new file mode 100644 index 0000000..fb0797a --- /dev/null +++ b/app/modules/connections/delete.php @@ -0,0 +1,41 @@ +single( + "SELECT id FROM connections WHERE id = ?", + "i", + [$connectionId] +); + +if (!$connection) { + $_SESSION['error'] = 'Verbindung nicht gefunden'; + header('Location: ?module=connections&action=list'); + exit; +} + +$rows = $sql->set( + "DELETE FROM connections WHERE id = ?", + "i", + [$connectionId] +); + +if ($rows > 0) { + $_SESSION['success'] = 'Verbindung geloescht'; +} else { + $_SESSION['error'] = 'Verbindung konnte nicht geloescht werden'; +} + +header('Location: ?module=connections&action=list'); +exit; diff --git a/app/modules/connections/list.php b/app/modules/connections/list.php index 192bc8f..9cb4ff3 100644 --- a/app/modules/connections/list.php +++ b/app/modules/connections/list.php @@ -2,19 +2,13 @@ /** * app/modules/connections/list.php * - * Übersicht der Netzwerkverbindungen - * - Tabellarische Liste aller Verbindungen - * - Filter nach Geräten, VLANs, Status - * - Später: Visuelle Netzwerk-Topologie + * Uebersicht der Netzwerkverbindungen */ -// ========================= -// Filter einlesen -// ========================= $search = trim($_GET['search'] ?? ''); $deviceId = (int)($_GET['device_id'] ?? 0); +$selectedConnectionId = (int)($_GET['connection_id'] ?? 0); -// Einheitliche Endpunkt-Aufloesung fuer polymorphe Port-Typen. $endpointUnionSql = " SELECT 'device' AS endpoint_type, @@ -60,16 +54,13 @@ $endpointUnionSql = " LEFT JOIN floors f ON f.id = fp.floor_id "; -// ========================= -// WHERE-Clause bauen -// ========================= $where = []; $types = ''; $params = []; if ($search !== '') { $where[] = "(e1.owner_name LIKE ? OR e2.owner_name LIKE ? OR e1.port_name LIKE ? OR e2.port_name LIKE ?)"; - $types .= "ssss"; + $types .= 'ssss'; $params[] = "%$search%"; $params[] = "%$search%"; $params[] = "%$search%"; @@ -78,16 +69,13 @@ if ($search !== '') { if ($deviceId > 0) { $where[] = "(e1.owner_device_id = ? OR e2.owner_device_id = ?)"; - $types .= "ii"; + $types .= 'ii'; $params[] = $deviceId; $params[] = $deviceId; } -$whereSql = $where ? "WHERE " . implode(" AND ", $where) : ""; +$whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : ''; -// ========================= -// Verbindungen laden -// ========================= $connections = $sql->get( "SELECT c.id, @@ -125,10 +113,22 @@ $connections = $sql->get( $params ); -// ========================= -// Filter-Daten -// ========================= -$devices = $sql->get("SELECT id, name FROM devices ORDER BY name", "", []); +$devices = $sql->get('SELECT id, name FROM devices ORDER BY name', '', []); + +$selectedConnection = null; +if ($selectedConnectionId > 0) { + foreach ((array)$connections as $entry) { + if ((int)($entry['id'] ?? 0) === $selectedConnectionId) { + $selectedConnection = $entry; + break; + } + } +} + +if ($selectedConnection === null && !empty($connections)) { + $selectedConnection = $connections[0]; + $selectedConnectionId = (int)($selectedConnection['id'] ?? 0); +} $selectedDevice = null; $selectedDevicePorts = []; @@ -140,7 +140,7 @@ if ($deviceId > 0) { FROM devices d LEFT JOIN device_types dt ON d.device_type_id = dt.id WHERE d.id = ?", - "i", + 'i', [$deviceId] ); @@ -159,7 +159,7 @@ if ($deviceId > 0) { JOIN device_ports dp ON dp.id = dpm.device_port_id WHERE dp.device_id = ? ) p", - "ii", + 'ii', [$deviceId, $deviceId] )['cnt'] ?? 0); @@ -187,7 +187,7 @@ if ($deviceId > 0) { OR (c.port_b_type = 'patchpanel' AND e2.endpoint_type = 'floor_patchpanel') ) WHERE e1.owner_device_id = ? OR e2.owner_device_id = ?", - "ii", + 'ii', [$deviceId, $deviceId] )['cnt'] ?? 0); @@ -207,7 +207,7 @@ if ($deviceId > 0) { ) p ORDER BY sort_id LIMIT 12", - "ii", + 'ii', [$deviceId, $deviceId] ); @@ -230,159 +230,247 @@ if ($deviceId > 0) { } } +$buildListUrl = static function (array $extra = []) use ($search, $deviceId): string { + $query = ['module' => 'connections', 'action' => 'list']; + if ($search !== '') { + $query['search'] = $search; + } + if ($deviceId > 0) { + $query['device_id'] = $deviceId; + } + foreach ($extra as $key => $value) { + if ($value === null || $value === '') { + continue; + } + $query[$key] = $value; + } + return '?' . http_build_query($query); +}; ?> -
-

Netzwerkverbindungen

+
+
+

Netzwerkverbindungen

- -
-
- - +
+ + + - + - + + + + + + + + Reset + + Neue Verbindung + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - Reset - + Neue Verbindung - + +
Von (Geraet -> Port)Nach (Geraet -> Port)VLANsBeschreibungStatusAktionen
+
+ +
+
+ +
+ + + + + + Warnung + + OK + + + Details + Bearbeiten + Von/Nach tauschen + + Loeschen + +
+ +
+

Keine Verbindungen gefunden.

+

+ + Erste Verbindung anlegen + +

+
+
- - - - - - - - - - - - - - - - - - + - - - - - - - - - - -
Von (Gerät → Port)Nach (Gerät → Port)VLANsBeschreibungStatusAktionen
-
- -
-
- -
- - - - - - - - - ⚠️ Warnung - + - Bearbeiten - Von/Nach tauschen - - Löschen - -
- - -
-

Keine Verbindungen gefunden.

-

- - Erste Verbindung anlegen - -

-
- +

+ +

Ports (max. 12)

+
    + +
  • + +
+ + +

Ausgewaehltes Geraet

+

Bitte ein Geraet im Filter auswaehlen.

+ + +
- + diff --git a/app/modules/dashboard/list.php b/app/modules/dashboard/list.php index 71c4593..1cdeb5e 100644 --- a/app/modules/dashboard/list.php +++ b/app/modules/dashboard/list.php @@ -1,15 +1,9 @@ $sql->single("SELECT COUNT(*) as cnt FROM devices", "", [])['cnt'] ?? 0, 'device_types' => $sql->single("SELECT COUNT(*) as cnt FROM device_types", "", [])['cnt'] ?? 0, @@ -18,7 +12,6 @@ $stats = [ 'locations' => $sql->single("SELECT COUNT(*) as cnt FROM locations", "", [])['cnt'] ?? 0, ]; -// Recent devices $recentDevices = $sql->get( "SELECT d.id, d.name, dt.name as type_name, r.name as rack_name, f.name as floor_name FROM devices d @@ -26,12 +19,41 @@ $recentDevices = $sql->get( LEFT JOIN racks r ON d.rack_id = r.id LEFT JOIN floors f ON r.floor_id = f.id ORDER BY d.id DESC LIMIT 5", - "", [] + "", + [] ); +$topologyDevices = $sql->get( + "SELECT + d.id AS device_id, + d.name AS device_name, + dt.name AS device_type_name, + r.id AS rack_id, + r.name AS rack_name, + f.id AS floor_id, + f.name AS floor_name + FROM devices d + LEFT JOIN device_types dt ON dt.id = d.device_type_id + LEFT JOIN racks r ON r.id = d.rack_id + LEFT JOIN floors f ON f.id = r.floor_id + ORDER BY floor_name, rack_name, device_name", + "", + [] +); + +$topologyPayload = array_map(static function (array $row): array { + return [ + 'device_id' => (int)($row['device_id'] ?? 0), + 'device_name' => (string)($row['device_name'] ?? ''), + 'device_type_name' => (string)($row['device_type_name'] ?? ''), + 'rack_id' => (int)($row['rack_id'] ?? 0), + 'rack_name' => (string)($row['rack_name'] ?? ''), + 'floor_id' => (int)($row['floor_id'] ?? 0), + 'floor_name' => (string)($row['floor_name'] ?? ''), + ]; +}, $topologyDevices); ?> -

Dashboard

@@ -39,35 +61,63 @@ $recentDevices = $sql->get(

- +
+
+

Gesamt-Topologie-Wand

+
+ + + +
+
+

Mausrad zoomt, Ziehen verschiebt. Klick auf einen Punkt zoomt auf den Rack-Kontext und oeffnet die Detailkarte.

+ + + + + + + + + + +
+
-

+

Standorte

- Verwalten → + Verwalten ->
-

-

Gerätetypen

- Verwalten → +

+

Geraetetypen

+ Verwalten ->
-

-

Geräte

- Verwalten → +

+

Geraete

+ Verwalten ->
-

+

Racks

- Verwalten → + Verwalten ->
- -

Zuletzt hinzugefügt

+

Zuletzt hinzugefuegt

@@ -82,20 +132,379 @@ $recentDevices = $sql->get( - - - - - + + + + +
BearbeitenBearbeiten
-

Noch keine Geräte vorhanden. Starten Sie mit Gerätetypen.

+

Noch keine Geraete vorhanden. Starten Sie mit Geraetetypen.

+ + + diff --git a/app/templates/header.php b/app/templates/header.php index 12e43fd..766327f 100644 --- a/app/templates/header.php +++ b/app/templates/header.php @@ -21,6 +21,7 @@ + From ec20fa2f969235ebdb88cdc9e92a2736c506f8db Mon Sep 17 00:00:00 2001 From: fixclean Date: Wed, 18 Feb 2026 09:29:57 +0100 Subject: [PATCH 3/9] docs: update open TODO status and patchpanel documentation - refresh TODO.md section for bugs/docs/status files - align implementation status for delete/auth progress - mark patchpanel infrastructure and floor SVG editor status in README - finalize database patchpanel status notes and init.sql connection rules --- IMPLEMENTATION_STATUS.md | 10 +++++----- README.md | 14 +++++++------- TODO.md | 20 ++++++++++---------- doc/DATABASE.md | 8 ++++---- init.sql | 5 ++++- 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/IMPLEMENTATION_STATUS.md b/IMPLEMENTATION_STATUS.md index 31143d3..fa00b55 100644 --- a/IMPLEMENTATION_STATUS.md +++ b/IMPLEMENTATION_STATUS.md @@ -70,16 +70,16 @@ 2. **Datenbank-Zugriff** - SQL-Klasse lädt und speichert Daten 3. **Responsive Design** - Alle Formulare und Listen sind formatiert 4. **Filter & Suche** - Alle Module haben Suchfunktionen -5. **CRUD-Operationen** - Create, Read, Update für alle Hauptmodule +5. **CRUD-Operationen** - Create, Read, Update, Delete für alle Hauptmodule --- ## ⚠️ Noch zu machen (Not-Must-Have) ### Höhere Priorität: -- [ ] **Delete-Funktionen** - Löschen noch als TODO (als AJAX implementieren) +- [x] **Delete-Funktionen** - Delete-Endpoints für Kernmodule inkl. `connections` und `floor_infrastructure` umgesetzt - [ ] **Fehlerbehandlung** - Error Pages, Validierungsmeldungen -- [ ] **Session/Auth** - Single-User Auth in bootstrap.php +- [x] **Session/Auth** - Single-User-Auth mit `requireAuth()` und `app/lib/auth.php` eingebunden - [ ] **SVG-Editor** - Interaktiver Floorplan-Editor für Räume/Dosen - [ ] **Port-Management** - Ports zu Geräten zuweisen @@ -106,7 +106,7 @@ app/ ├── lib/ │ ├── _sql.php ✅ DB-Wrapper │ ├── helpers.php ✅ Utility-Funktionen -│ └── auth.php 🚧 TODO: Auth +│ └── auth.php ✅ Auth-Helper + requireAuth() ├── templates/ │ ├── layout.php ✅ HTML-Layout │ ├── header.php ✅ Header/Nav @@ -132,7 +132,7 @@ app/ ## 💡 Nächste Schritte (empfohlen) 1. **Testen Sie die Module** - Probieren Sie Anlegen/Bearbeiten aus -2. **Implementieren Sie Delete-Funktionen** - Mit AJAX oder POST +2. **Delete-Flows prüfen** - Sonderfälle und Fehlermeldungen bei Abhängigkeiten testen 3. **Bessere Fehlerbehandlung** - Sessions für Error-Messages 4. **Mobile-Optimierung** - Responsive Verbesserungen 5. **SVG-Editor für Floorplans** - Visuelles Raumdesign diff --git a/README.md b/README.md index 085703c..278fa07 100644 --- a/README.md +++ b/README.md @@ -238,19 +238,19 @@ Verbindungen werden: - Die untereinander verbundenen Patchpanels lassen sich direkt auf der SVG-Stockwerkskarte verorten, damit jeder Port physisch nachvollziehbar bleibt. - Verbindungen zu Racks / Switches darstellbar -### TODO: Patchpanel-Infrastruktur -- [ ] Floorplans erweitern, damit Patchpanels als feste Infrastrukturobjekte (nicht als rack-basierte `devices`) angelegt, verschoben und mit `x/y`/Größe verankert werden. -- [ ] Backend und SVG-Editor dahingehend adaptieren, dass Patchpanel-Ports unabhängig von Racks definiert werden können. -- [ ] Patchpanel ↔ Patchpanel- und Patchpanel ↔ Netzwerkbuchse-Verbindungen als permanente Kabel zwischen Floorplan-Objekten darstellen und über die `connections`-Tabelle verwalten. -- [ ] UI/Schema-Dokumentation aktualisieren (README + Datenbank-Docs) sowie neue SQL-Tabellen (`floor_patchpanels` / `floor_patchpanel_ports`) fertigstellen. -- [ ] Floorplan-Filter/Legend leiten die Nutzung dieser Infrastrukturobjekte, Kampagnen und Search & Filter integrieren. +### Patchpanel-Infrastruktur (Status: 18. Februar 2026) +- [x] Floorplans erweitert: Patchpanels können als feste Infrastrukturobjekte (ohne Rack-Device) inkl. `x/y` und Größe verwaltet werden. +- [x] Backend + SVG-Editor angepasst: Patchpanel-Ports werden über `floor_patchpanel_ports` gepflegt. +- [x] Patchpanel ↔ Patchpanel und Patchpanel ↔ Netzwerkbuchse werden über `connections` verwaltet. +- [x] SQL-Tabellen `floor_patchpanels` / `floor_patchpanel_ports` sind im Schema enthalten. +- [ ] Floorplan-Filter/Legend und erweiterte Suche für Infrastrukturobjekte weiter ausbauen. ### Stockwerksinfrastruktur-Modul - Das neue Modul „Stockwerksinfrastruktur“ sammelt Patchpanels und Wandbuchsen an einem Ort. - Patchfelder bekommen feste X/Y-Positionen, Maße, Portanzahl und verknüpfen zu Floorplans. - Wandbuchsen sind direkt mit Räumen verbunden, können aber auch später im SVG verteilt werden. - Ziel: Die Floorplan-Grafik zeigt die permanente Infrastruktur samt fest verlegter Kabelverläufe. -- TODO: SVG-Editor um Drag & Drop für diese Objekte erweitern und Klicks direkt mit dem Modul verbinden. +- [x] SVG-Editor für diese Objekte ist mit Drag & Drop umgesetzt und direkt mit dem Modul verbunden. --- diff --git a/TODO.md b/TODO.md index 6cf7f5b..8759b0a 100644 --- a/TODO.md +++ b/TODO.md @@ -20,17 +20,17 @@ Quelle: vorhandene `TODO`-Marker im Repository plus offene Architekturpunkte. ## Offen (Bugs / Doku / Statusdateien) -- [ ] `BUGS.md:3`: Design vereinheitlichen. -- [ ] `IMPLEMENTATION_STATUS.md:80`: Delete-Funktionen-Status aktualisieren. -- [ ] `IMPLEMENTATION_STATUS.md:109`: Auth-Status aktualisieren. -- [ ] `README.md:241`: Patchpanel-Infrastruktur umsetzen und danach Doku abhaken. -- [ ] `README.md:253`: SVG-Editor fuer Floor-Infrastruktur (Drag-and-Drop + direkte Modul-Interaktion). -- [ ] `doc/DATABASE.md:127`: TODO-Abschnitt fuer Patchpanel/Floorplan finalisieren. -- [ ] `init.sql:379`: Kommentar zur Port-Konfiguration in verbindliche Implementierungsregeln ueberfuehren. +- [ ] `BUGS.md`: Design vereinheitlichen. +- [x] `IMPLEMENTATION_STATUS.md`: Delete-Funktionen-Status aktualisiert. +- [x] `IMPLEMENTATION_STATUS.md`: Auth-Status aktualisiert. +- [x] `README.md`: Patchpanel-Infrastruktur-Status nachgezogen. +- [x] `README.md`: SVG-Editor-Status fuer Floor-Infrastruktur nachgezogen. +- [x] `doc/DATABASE.md`: Statusabschnitt fuer Patchpanel/Floorplan finalisiert. +- [x] `init.sql`: Port-Konfigurationsregeln konkretisiert. ## Topologie-Backlog (ausstehend) -- [ ] `connections.port_a_type` / `connections.port_b_type` um `patchpanel` erweitern und auf `floor_patchpanel_ports.id` referenzieren. +- [x] `connections.port_a_type` / `connections.port_b_type` um `patchpanel` erweitert und auf `floor_patchpanel_ports.id` nutzbar gemacht. - [ ] Validierungsregeln fuer Topologie fest verdrahten (Patchpanel-Port nur mit Patchpanel-Port oder Netzwerkbuchsen-Port). -- [ ] Port-CRUD fuer Patchpanels: `floor_patchpanel_ports` aus `port_count` erzeugen/synchronisieren. -- [ ] Port-CRUD fuer Netzwerkbuchsen: `network_outlet_ports` pflegen (mindestens ein Port je Buchse) und in Verbindungen nutzbar machen. +- [x] Port-CRUD fuer Patchpanels: `floor_patchpanel_ports` wird aus `port_count` erzeugt/synchronisiert. +- [x] Port-CRUD fuer Netzwerkbuchsen: `network_outlet_ports` wird gepflegt (mindestens ein Port je Buchse) und ist in Verbindungen nutzbar. diff --git a/doc/DATABASE.md b/doc/DATABASE.md index 893cfe6..62888d6 100644 --- a/doc/DATABASE.md +++ b/doc/DATABASE.md @@ -124,10 +124,10 @@ Die Bühne für Patchpanel-Objekte auf dem Stockwerkplan. - Attributes: Panel-Referenz, `name`, `port_type_id`, optionale VLAN- bzw. Status-Attribute. - Ports werden über `connections` sowohl mit anderen Patchpanels als auch mit Netzwerkbuchsen (`network_outlet_ports`) oder Gerätports verbunden; dadurch lassen sich Router-Kabel grafisch darstellen. -**TODO** -- [ ] Floorplan- und CRUD-Module so erweitern, dass Patchpanels als Floor-Objekte verwaltet und deren Ports gepflegt werden können (`floor_patchpanels`, `floor_patchpanel_ports`). -- [ ] Verbindungen zwischen Patchpanel ↔ Patchpanel und Patchpanel ↔ Netzwerkbuchse standardisiert in der `connections`-Logik abbilden. -- [ ] UI/CSV/Export/Dokumentation nachziehen, damit Planer sofort sehen, wo die permanent installierten Kabel verlaufen. +**Status (18. Februar 2026)** +- [x] Floorplan- und CRUD-Module wurden für Patchpanels als Floor-Objekte inkl. Port-Pflege erweitert (`floor_patchpanels`, `floor_patchpanel_ports`). +- [x] Verbindungen zwischen Patchpanel ↔ Patchpanel und Patchpanel ↔ Netzwerkbuchse sind in der `connections`-Logik abbildbar. +- [ ] UI/CSV/Export-Dokumentation weiter ausbauen, damit Planer Kabelverläufe direkt auswerten können. --- diff --git a/init.sql b/init.sql index 4b0252b..fd77e2d 100644 --- a/init.sql +++ b/init.sql @@ -376,7 +376,10 @@ CREATE TABLE `floor_patchpanel_ports` ( `comment` text DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci; --- TODO: Port-Konfiguration (Patchpanel ↔ Patchpanel, Patchpanel ↔ Netzwerkbuchse) wird über die `connections`-Tabelle geregelt. +-- Port-Regeln fuer Patchpanel-Verbindungen: +-- 1) `connections.port_a_type` / `connections.port_b_type` nutzen den Wert `patchpanel` fuer IDs aus `floor_patchpanel_ports`. +-- 2) Patchpanel-Ports duerfen mit Patchpanel-Ports, Netzwerkbuchsen-Ports (`outlet`) oder Geraete-/Modulports verbunden werden. +-- 3) Die fachliche Validierung erfolgt in den Save-Handlern der Verbindungs-Module; das Schema bleibt polymorph. -- -------------------------------------------------------- From f4ce7f360d2db9cee7eff9ad67abed2c166f7bc4 Mon Sep 17 00:00:00 2001 From: fixclean Date: Wed, 18 Feb 2026 09:40:59 +0100 Subject: [PATCH 4/9] feat: implement package 1 session and validation feedback - add session validation_errors bootstrap initialization - render global flash + validation messages in header - remove footer alert-based flash handling - persist structured validation errors across save handlers - mark NEXT_STEPS package 1 tasks as done --- NEXT_STEPS.md | 8 ++-- app/assets/css/app.css | 38 ++++++++++++++- app/bootstrap.php | 4 ++ app/modules/buildings/save.php | 1 + app/modules/connections/save.php | 2 + app/modules/device_types/save.php | 3 ++ app/modules/devices/save.php | 1 + app/modules/floor_infrastructure/save.php | 38 +++++++++++++++ app/modules/floors/save.php | 2 + app/modules/locations/save.php | 1 + app/modules/port_types/edit.php | 17 ------- app/modules/port_types/list.php | 18 +------- app/modules/port_types/save.php | 1 + app/modules/racks/save.php | 1 + app/modules/rooms/save.php | 10 ++++ app/templates/footer.php | 16 ------- app/templates/header.php | 56 +++++++++++++++++++++++ 17 files changed, 162 insertions(+), 55 deletions(-) diff --git a/NEXT_STEPS.md b/NEXT_STEPS.md index d4830a5..3e82073 100644 --- a/NEXT_STEPS.md +++ b/NEXT_STEPS.md @@ -3,10 +3,10 @@ ## 🎯 Für die nächsten Sessions ### Package 1: Fehlerbehandlung & Sessions (1-2h) -- [ ] Session-Handling in `bootstrap.php` implementieren -- [ ] Error-Messages in Session speichern ($SESSION['error'], $SESSION['success']) -- [ ] Header mit Fehlermeldungen in Layout -- [ ] Validierungsfehler anzeigen +- [x] Session-Handling in `bootstrap.php` implementieren +- [x] Error-Messages in Session speichern (`$_SESSION['error']`, `$_SESSION['success']`) +- [x] Header mit Fehlermeldungen in Layout +- [x] Validierungsfehler anzeigen ### Package 2: Delete-Funktionen (1h) - [ ] DELETE-Endpoints für alle Module diff --git a/app/assets/css/app.css b/app/assets/css/app.css index fa37a8f..b37b5b6 100644 --- a/app/assets/css/app.css +++ b/app/assets/css/app.css @@ -77,6 +77,42 @@ main { min-height: calc(100vh - 200px); } +.flash-stack { + display: grid; + gap: 10px; + margin: 0 auto 18px; + max-width: 1200px; +} + +.flash-message { + border-radius: 8px; + border: 1px solid transparent; + padding: 12px 14px; + background: #f8fafc; +} + +.flash-message--success { + border-color: #99dfba; + background: #ebf9f1; + color: #165938; +} + +.flash-message--error { + border-color: #efb4b4; + background: #fff1f1; + color: #8a1f1f; +} + +.flash-message__text { + margin: 0; + font-weight: 600; +} + +.flash-message__list { + margin: 8px 0 0 18px; + padding: 0; +} + /* Shared components -------------------------------------------------- */ .filter-form { margin: 20px 0; @@ -192,4 +228,4 @@ main { footer>p { margin-bottom: 0; -} \ No newline at end of file +} diff --git a/app/bootstrap.php b/app/bootstrap.php index fbb71df..e19966c 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -12,6 +12,10 @@ if (session_status() !== PHP_SESSION_ACTIVE) { session_start(); } +if (!isset($_SESSION['validation_errors']) || !is_array($_SESSION['validation_errors'])) { + $_SESSION['validation_errors'] = []; +} + require_once __DIR__ . '/lib/_sql.php'; $sql = new SQL(); diff --git a/app/modules/buildings/save.php b/app/modules/buildings/save.php index 2aa4b50..61809f6 100644 --- a/app/modules/buildings/save.php +++ b/app/modules/buildings/save.php @@ -25,6 +25,7 @@ if ($locationId <= 0) { if (!empty($errors)) { $_SESSION['error'] = implode(', ', $errors); + $_SESSION['validation_errors'] = $errors; $redirectUrl = $buildingId ? "?module=buildings&action=edit&id=$buildingId" : "?module=buildings&action=edit"; header("Location: $redirectUrl"); exit; diff --git a/app/modules/connections/save.php b/app/modules/connections/save.php index d56efd2..8f0a51d 100644 --- a/app/modules/connections/save.php +++ b/app/modules/connections/save.php @@ -88,6 +88,7 @@ if ($isEndpointUsed($portBType, $portBId)) { if (!empty($errors)) { $_SESSION['error'] = implode(', ', $errors); + $_SESSION['validation_errors'] = $errors; $redirectUrl = $connId ? "?module=connections&action=edit&id=$connId" : "?module=connections&action=edit"; header("Location: $redirectUrl"); exit; @@ -123,6 +124,7 @@ if ($connId > 0) { if ($connectionTypeId <= 0) { $_SESSION['error'] = "Kein Verbindungstyp verfuegbar"; + $_SESSION['validation_errors'] = ["Kein Verbindungstyp verfuegbar"]; header("Location: ?module=connections&action=edit"); exit; } diff --git a/app/modules/device_types/save.php b/app/modules/device_types/save.php index ec6bfc5..c7c1432 100644 --- a/app/modules/device_types/save.php +++ b/app/modules/device_types/save.php @@ -49,6 +49,7 @@ if (!in_array($category, ['switch', 'server', 'patchpanel', 'other'])) { // Falls Fehler: zurück zum Edit-Formular if (!empty($errors)) { $_SESSION['error'] = implode(', ', $errors); + $_SESSION['validation_errors'] = $errors; header('Location: ?module=device_types&action=edit' . ($deviceTypeId ? "&id=$deviceTypeId" : "")); exit; } @@ -67,6 +68,7 @@ if (!empty($_FILES['image']['name'])) { // Nur SVG, JPG, PNG erlaubt if (!in_array($fileExt, ['svg', 'jpg', 'jpeg', 'png'])) { $_SESSION['error'] = "Nur SVG, JPG und PNG sind erlaubt"; + $_SESSION['validation_errors'] = ["Nur SVG, JPG und PNG sind erlaubt"]; header('Location: ?module=device_types&action=edit' . ($deviceTypeId ? "&id=$deviceTypeId" : "")); exit; } @@ -86,6 +88,7 @@ if (!empty($_FILES['image']['name'])) { $imageType = $fileExt === 'svg' ? 'svg' : 'bitmap'; } else { $_SESSION['error'] = "Datei-Upload fehlgeschlagen"; + $_SESSION['validation_errors'] = ["Datei-Upload fehlgeschlagen"]; header('Location: ?module=device_types&action=edit' . ($deviceTypeId ? "&id=$deviceTypeId" : "")); exit; } diff --git a/app/modules/devices/save.php b/app/modules/devices/save.php index c10e2d5..18ccd6f 100644 --- a/app/modules/devices/save.php +++ b/app/modules/devices/save.php @@ -57,6 +57,7 @@ if ($rackHeightHe < 1) { // Falls Fehler: zurück zum Edit-Formular if (!empty($errors)) { $_SESSION['error'] = implode(', ', $errors); + $_SESSION['validation_errors'] = $errors; $redirectUrl = $deviceId ? "?module=devices&action=edit&id=$deviceId" : "?module=devices&action=edit"; header("Location: $redirectUrl"); exit; diff --git a/app/modules/floor_infrastructure/save.php b/app/modules/floor_infrastructure/save.php index c416bee..0126dd8 100644 --- a/app/modules/floor_infrastructure/save.php +++ b/app/modules/floor_infrastructure/save.php @@ -20,6 +20,24 @@ if ($type === 'patchpanel') { $height = $fixedPanelHeight; $portCount = (int)($_POST['port_count'] ?? 0); $comment = trim($_POST['comment'] ?? ''); + $errors = []; + + if ($name === '') { + $errors[] = 'Name ist erforderlich'; + } + if ($floorId <= 0) { + $errors[] = 'Stockwerk ist erforderlich'; + } + if ($portCount < 0) { + $errors[] = 'Port-Anzahl darf nicht negativ sein'; + } + if (!empty($errors)) { + $_SESSION['error'] = implode(', ', $errors); + $_SESSION['validation_errors'] = $errors; + $redirectUrl = $id > 0 ? "?module=floor_infrastructure&action=edit&type=patchpanel&id=$id" : "?module=floor_infrastructure&action=edit&type=patchpanel"; + header("Location: $redirectUrl"); + exit; + } $panelId = $id; @@ -55,6 +73,7 @@ if ($type === 'patchpanel') { } } } + $_SESSION['success'] = $id > 0 ? 'Patchpanel gespeichert' : 'Patchpanel erstellt'; } elseif ($type === 'outlet') { $name = trim($_POST['name'] ?? ''); $roomId = (int)($_POST['room_id'] ?? 0); @@ -62,6 +81,21 @@ if ($type === 'patchpanel') { $y = (int)($_POST['y'] ?? 0); $comment = trim($_POST['comment'] ?? ''); $outletId = $id; + $errors = []; + + if ($name === '') { + $errors[] = 'Name ist erforderlich'; + } + if ($roomId <= 0) { + $errors[] = 'Raum ist erforderlich'; + } + if (!empty($errors)) { + $_SESSION['error'] = implode(', ', $errors); + $_SESSION['validation_errors'] = $errors; + $redirectUrl = $id > 0 ? "?module=floor_infrastructure&action=edit&type=outlet&id=$id" : "?module=floor_infrastructure&action=edit&type=outlet"; + header("Location: $redirectUrl"); + exit; + } if ($id > 0) { $sql->set( @@ -93,6 +127,10 @@ if ($type === 'patchpanel') { ); } } + $_SESSION['success'] = $id > 0 ? 'Wandbuchse gespeichert' : 'Wandbuchse erstellt'; +} else { + $_SESSION['error'] = 'Ungueltiger Infrastrukturobjekt-Typ'; + $_SESSION['validation_errors'] = ['Ungueltiger Infrastrukturobjekt-Typ']; } header('Location: ?module=floor_infrastructure&action=list'); diff --git a/app/modules/floors/save.php b/app/modules/floors/save.php index 0bd3012..55ed0da 100644 --- a/app/modules/floors/save.php +++ b/app/modules/floors/save.php @@ -37,6 +37,7 @@ if ($buildingId <= 0) { // Falls Fehler: zurück zum Edit-Formular if (!empty($errors)) { $_SESSION['error'] = implode(', ', $errors); + $_SESSION['validation_errors'] = $errors; $redirectUrl = $floorId ? "?module=floors&action=edit&id=$floorId" : "?module=floors&action=edit"; header("Location: $redirectUrl"); exit; @@ -50,6 +51,7 @@ if ($floorSvgContent !== '') { $storedSvgPath = storeSvgEditorContent($sql, $floorId, $floorSvgContent); if ($storedSvgPath === false) { $_SESSION['error'] = "SVG aus dem Editor konnte nicht gespeichert werden"; + $_SESSION['validation_errors'] = ["SVG aus dem Editor konnte nicht gespeichert werden"]; $redirectUrl = $floorId ? "?module=floors&action=edit&id=$floorId" : "?module=floors&action=edit"; header("Location: $redirectUrl"); exit; diff --git a/app/modules/locations/save.php b/app/modules/locations/save.php index 6245513..1ae21ee 100644 --- a/app/modules/locations/save.php +++ b/app/modules/locations/save.php @@ -20,6 +20,7 @@ if (empty($name)) { if (!empty($errors)) { $_SESSION['error'] = implode(', ', $errors); + $_SESSION['validation_errors'] = $errors; $redirectUrl = $locationId ? "?module=locations&action=edit&id=$locationId" : "?module=locations&action=edit"; header("Location: $redirectUrl"); exit; diff --git a/app/modules/port_types/edit.php b/app/modules/port_types/edit.php index 9334b4d..68ca3ce 100644 --- a/app/modules/port_types/edit.php +++ b/app/modules/port_types/edit.php @@ -23,20 +23,11 @@ $isEdit = !empty($portType); $pageTitle = $isEdit ? "Porttyp bearbeiten: " . htmlspecialchars($portType['name']) : "Neuen Porttyp anlegen"; $mediaOptions = ['copper' => 'Kupfer', 'fiber' => 'Lichtwelle', 'coax' => 'Koax', 'other' => 'Sonstiges']; -$error = $_SESSION['error'] ?? ''; -unset($_SESSION['error']); - ?>

- -
- -
- -
@@ -157,12 +148,4 @@ unset($_SESSION['error']); opacity: 0.8; } -.error-message { - background: #ffe3e3; - color: #a73737; - border: 1px solid #f5c2c2; - padding: 10px; - border-radius: 4px; - margin-bottom: 15px; -} diff --git a/app/modules/port_types/list.php b/app/modules/port_types/list.php index d0a7725..e462d00 100644 --- a/app/modules/port_types/list.php +++ b/app/modules/port_types/list.php @@ -34,20 +34,11 @@ $portTypes = $sql->get( $params ); -$success = $_SESSION['success'] ?? ''; -unset($_SESSION['success']); - ?>

Porttypen

- -
- -
- -
@@ -164,8 +155,7 @@ unset($_SESSION['success']); font-weight: bold; } -.empty-state, -.success-message { +.empty-state { margin: 20px 0; padding: 15px; border-radius: 6px; @@ -177,12 +167,6 @@ unset($_SESSION['success']); text-align: center; } -.success-message { - background: #e9f8f1; - border: 1px solid #c7eedc; - color: #2c7d59; -} - .actions { white-space: nowrap; } diff --git a/app/modules/port_types/save.php b/app/modules/port_types/save.php index 9461db3..124233e 100644 --- a/app/modules/port_types/save.php +++ b/app/modules/port_types/save.php @@ -29,6 +29,7 @@ if (!in_array($medium, $allowedMediums, true)) { if (!empty($errors)) { $_SESSION['error'] = implode(', ', $errors); + $_SESSION['validation_errors'] = $errors; $redirect = $portTypeId ? "?module=port_types&action=edit&id=$portTypeId" : "?module=port_types&action=edit"; header("Location: $redirect"); exit; diff --git a/app/modules/racks/save.php b/app/modules/racks/save.php index 6dc0d59..edc0fe3 100644 --- a/app/modules/racks/save.php +++ b/app/modules/racks/save.php @@ -40,6 +40,7 @@ if ($heightHe < 1) { // Falls Fehler: zurück zum Edit-Formular if (!empty($errors)) { $_SESSION['error'] = implode(', ', $errors); + $_SESSION['validation_errors'] = $errors; $redirectUrl = $rackId ? "?module=racks&action=edit&id=$rackId" : "?module=racks&action=edit"; header("Location: $redirectUrl"); exit; diff --git a/app/modules/rooms/save.php b/app/modules/rooms/save.php index c4cf750..bf01a73 100644 --- a/app/modules/rooms/save.php +++ b/app/modules/rooms/save.php @@ -18,6 +18,15 @@ $comment = trim((string)($_POST['comment'] ?? '')); $rawPolygon = trim((string)($_POST['polygon_points'] ?? '')); if ($name === '' || $floorId <= 0) { + $errors = []; + if ($name === '') { + $errors[] = 'Name ist erforderlich'; + } + if ($floorId <= 0) { + $errors[] = 'Stockwerk ist erforderlich'; + } + $_SESSION['error'] = implode(', ', $errors); + $_SESSION['validation_errors'] = $errors; $redirect = $roomId > 0 ? "?module=rooms&action=edit&id=$roomId" : "?module=rooms&action=edit&floor_id=$floorId"; header("Location: $redirect"); exit; @@ -100,6 +109,7 @@ if (roomsHasPolygonColumn($sql)) { } } +$_SESSION['success'] = $roomId > 0 ? 'Raum gespeichert' : 'Raum erstellt'; header('Location: ?module=locations&action=list'); exit; diff --git a/app/templates/footer.php b/app/templates/footer.php index 6e333c5..172e10d 100644 --- a/app/templates/footer.php +++ b/app/templates/footer.php @@ -12,21 +12,5 @@ | Session:

- - - - - diff --git a/app/templates/header.php b/app/templates/header.php index 766327f..6478ff4 100644 --- a/app/templates/header.php +++ b/app/templates/header.php @@ -64,4 +64,60 @@ + 'success', + 'text' => $successMessage, + ]; +} + +$errorMessage = trim((string)($_SESSION['error'] ?? '')); +if ($errorMessage !== '') { + $flashMessages[] = [ + 'type' => 'error', + 'text' => $errorMessage, + ]; +} + +$validationErrors = $_SESSION['validation_errors'] ?? []; +if (!is_array($validationErrors)) { + $validationErrors = []; +} +$validationErrors = array_values(array_filter(array_map(static function ($entry) { + return trim((string)$entry); +}, $validationErrors), static function ($entry) { + return $entry !== ''; +})); + +if (!empty($validationErrors)) { + $flashMessages[] = [ + 'type' => 'error', + 'text' => 'Bitte pruefe die Eingaben:', + 'details' => $validationErrors, + ]; +} + +unset($_SESSION['success'], $_SESSION['error'], $_SESSION['validation_errors']); +?> +
+ +
+ +
+

+ +
    + +
  • + +
+ +
+ +
+ From 77758f71d386e2753375dab78e98f9153a896182 Mon Sep 17 00:00:00 2001 From: fixclean Date: Wed, 18 Feb 2026 10:16:24 +0100 Subject: [PATCH 5/9] feat: complete package 2 delete flows and package 3 port management --- NEXT_STEPS.md | 14 +- app/modules/connections/delete.php | 28 ++- app/modules/connections/list.php | 48 ++++- app/modules/devices/delete.php | 47 ++++- app/modules/devices/edit.php | 148 ++++++++++++-- app/modules/devices/list.php | 213 +++++++++++--------- app/modules/devices/save.php | 106 ++++++++++ app/modules/floor_infrastructure/delete.php | 28 ++- app/modules/floor_infrastructure/list.php | 53 ++++- app/modules/floors/delete.php | 51 ++++- app/modules/floors/list.php | 25 ++- app/modules/locations/delete.php | 10 + app/modules/port_types/delete.php | 84 ++++++++ app/modules/port_types/list.php | 40 ++++ 14 files changed, 754 insertions(+), 141 deletions(-) create mode 100644 app/modules/port_types/delete.php diff --git a/NEXT_STEPS.md b/NEXT_STEPS.md index 3e82073..efbd2e8 100644 --- a/NEXT_STEPS.md +++ b/NEXT_STEPS.md @@ -9,15 +9,15 @@ - [x] Validierungsfehler anzeigen ### Package 2: Delete-Funktionen (1h) -- [ ] DELETE-Endpoints für alle Module -- [ ] AJAX-Bestätigung vor Löschen -- [ ] Kaskadierendes Löschen prüfen (z.B. Floor → Racks) +- [x] DELETE-Endpoints für alle Module +- [x] AJAX-Bestätigung vor Löschen +- [x] Kaskadierendes Löschen prüfen (z.B. Floor → Racks) ### Package 3: Port-Management (2-3h) -- [ ] Ports zu Device-Types verwalten -- [ ] Ports zu Devices anzeigen -- [ ] Port-Status (aktiv/inaktiv) -- [ ] VLAN-Zuordnung zu Ports +- [x] Ports zu Device-Types verwalten +- [x] Ports zu Devices anzeigen +- [x] Port-Status (aktiv/inaktiv) +- [x] VLAN-Zuordnung zu Ports ### Package 4: SVG-Editor für Floorplans (4-5h) - [ ] Interaktiver SVG-Editor für Rooms diff --git a/app/modules/connections/delete.php b/app/modules/connections/delete.php index fb0797a..a4d25c8 100644 --- a/app/modules/connections/delete.php +++ b/app/modules/connections/delete.php @@ -2,12 +2,19 @@ /** * app/modules/connections/delete.php * - * Loescht eine Verbindung und leitet zur Liste zurueck. + * Loescht eine Verbindung (AJAX-POST bevorzugt, GET-Fallback fuer Redirects). */ -$connectionId = (int)($_GET['id'] ?? $_POST['id'] ?? 0); +$isPost = ($_SERVER['REQUEST_METHOD'] ?? '') === 'POST'; +$connectionId = (int)($_POST['id'] ?? $_GET['id'] ?? 0); if ($connectionId <= 0) { + if ($isPost) { + header('Content-Type: application/json; charset=utf-8'); + http_response_code(400); + echo json_encode(['success' => false, 'message' => 'Ungueltige Verbindungs-ID']); + exit; + } $_SESSION['error'] = 'Ungueltige Verbindungs-ID'; header('Location: ?module=connections&action=list'); exit; @@ -20,6 +27,12 @@ $connection = $sql->single( ); if (!$connection) { + if ($isPost) { + header('Content-Type: application/json; charset=utf-8'); + http_response_code(404); + echo json_encode(['success' => false, 'message' => 'Verbindung nicht gefunden']); + exit; + } $_SESSION['error'] = 'Verbindung nicht gefunden'; header('Location: ?module=connections&action=list'); exit; @@ -31,6 +44,17 @@ $rows = $sql->set( [$connectionId] ); +if ($isPost) { + header('Content-Type: application/json; charset=utf-8'); + if ($rows > 0) { + echo json_encode(['success' => true, 'message' => 'Verbindung geloescht']); + } else { + http_response_code(500); + echo json_encode(['success' => false, 'message' => 'Verbindung konnte nicht geloescht werden']); + } + exit; +} + if ($rows > 0) { $_SESSION['success'] = 'Verbindung geloescht'; } else { diff --git a/app/modules/connections/list.php b/app/modules/connections/list.php index 9cb4ff3..abde9c5 100644 --- a/app/modules/connections/list.php +++ b/app/modules/connections/list.php @@ -335,11 +335,12 @@ $buildListUrl = static function (array $extra = []) use ($search, $deviceId): st Details Bearbeiten Von/Nach tauschen - + @@ -382,7 +383,12 @@ $buildListUrl = static function (array $extra = []) use ($search, $deviceId): st

Keine Verbindung ausgewaehlt.

@@ -474,3 +480,35 @@ $buildListUrl = static function (array $extra = []) use ($search, $deviceId): st } } + + diff --git a/app/modules/devices/delete.php b/app/modules/devices/delete.php index 12992be..a4e66da 100644 --- a/app/modules/devices/delete.php +++ b/app/modules/devices/delete.php @@ -3,12 +3,20 @@ * app/modules/devices/delete.php * * Loescht ein Geraet. Bei Abhaengigkeiten ist force=1 erforderlich. + * Unterstuetzt GET-Redirects und AJAX-POST. */ -$deviceId = (int)($_GET['id'] ?? 0); -$forceDelete = (int)($_GET['force'] ?? 0) === 1; +$isPost = ($_SERVER['REQUEST_METHOD'] ?? '') === 'POST'; +$deviceId = (int)($_POST['id'] ?? $_GET['id'] ?? 0); +$forceDelete = (int)($_POST['force'] ?? $_GET['force'] ?? 0) === 1; if ($deviceId <= 0) { + if ($isPost) { + header('Content-Type: application/json; charset=utf-8'); + http_response_code(400); + echo json_encode(['success' => false, 'message' => 'Ungueltige Geraete-ID']); + exit; + } $_SESSION['error'] = "Ungueltige Geraete-ID"; header('Location: ?module=devices&action=list'); exit; @@ -21,6 +29,12 @@ $device = $sql->single( ); if (!$device) { + if ($isPost) { + header('Content-Type: application/json; charset=utf-8'); + http_response_code(404); + echo json_encode(['success' => false, 'message' => 'Geraet nicht gefunden']); + exit; + } $_SESSION['error'] = "Geraet nicht gefunden"; header('Location: ?module=devices&action=list'); exit; @@ -70,7 +84,23 @@ if ($hasDependencies && !$forceDelete) { $parts[] = $moduleCount . ' Port-Module'; } - $_SESSION['error'] = "Geraet hat abhaengige Daten (" . implode(', ', $parts) . "). Loeschen bitte bestaetigen."; + $dependencyMessage = "Geraet hat abhaengige Daten (" . implode(', ', $parts) . "). Loeschen bitte bestaetigen."; + if ($isPost) { + header('Content-Type: application/json; charset=utf-8'); + http_response_code(409); + echo json_encode([ + 'success' => false, + 'requires_force' => true, + 'message' => $dependencyMessage, + 'dependencies' => [ + 'connections' => $connectionCount, + 'ports' => $portCount, + 'modules' => $moduleCount + ] + ]); + exit; + } + $_SESSION['error'] = $dependencyMessage; header('Location: ?module=devices&action=edit&id=' . urlencode((string)$deviceId)); exit; } @@ -91,8 +121,19 @@ $deleted = $sql->set( ); if ($deleted > 0) { + if ($isPost) { + header('Content-Type: application/json; charset=utf-8'); + echo json_encode(['success' => true, 'message' => "Geraet geloescht: " . $device['name']]); + exit; + } $_SESSION['success'] = "Geraet geloescht: " . $device['name']; } else { + if ($isPost) { + header('Content-Type: application/json; charset=utf-8'); + http_response_code(500); + echo json_encode(['success' => false, 'message' => 'Geraet konnte nicht geloescht werden']); + exit; + } $_SESSION['error'] = "Geraet konnte nicht geloescht werden"; } diff --git a/app/modules/devices/edit.php b/app/modules/devices/edit.php index 192e4a1..72d87cf 100644 --- a/app/modules/devices/edit.php +++ b/app/modules/devices/edit.php @@ -64,6 +64,19 @@ if ($isEdit) { // ========================= $deviceTypes = $sql->get("SELECT id, name, category FROM device_types ORDER BY name", "", []); $racks = $sql->get("SELECT id, name FROM racks ORDER BY name", "", []); +$devicePorts = []; + +if ($isEdit) { + $devicePorts = $sql->get( + "SELECT dp.id, dp.name, dp.status, dp.mode, dp.vlan_config, pt.name AS port_type_name + FROM device_ports dp + LEFT JOIN port_types pt ON pt.id = dp.port_type_id + WHERE dp.device_id = ? + ORDER BY dp.id", + "i", + [$deviceId] + ); +} ?> @@ -160,6 +173,67 @@ $racks = $sql->get("SELECT id, name FROM racks ORDER BY name", "", []);
+
+ Ports + + +

Portstatus und VLAN-Zuordnung koennen hier direkt gepflegt werden (VLANs kommagetrennt, z. B. 10,20,30).

+ + + + + + + + + + + + + + + + + + + + + + +
NamePort-TypStatusModusVLANs
+ + + + + + + +
+ +

Zu diesem Geraet sind aktuell keine Ports vorhanden.

+ + +

Ports werden nach dem ersten Speichern automatisch aus dem Geraetetyp erzeugt und koennen dann hier gepflegt werden.

+ +
+ @@ -264,34 +338,64 @@ $racks = $sql->get("SELECT id, name FROM racks ORDER BY name", "", []); .button:hover { opacity: 0.8; } + +.device-port-table { + width: 100%; + border-collapse: collapse; + margin-top: 10px; +} + +.device-port-table th, +.device-port-table td { + border-bottom: 1px solid #ddd; + padding: 8px; + text-align: left; +} + +.device-port-table input, +.device-port-table select { + width: 100%; + min-width: 120px; +} diff --git a/app/modules/devices/list.php b/app/modules/devices/list.php index 39ebcdf..c41a448 100644 --- a/app/modules/devices/list.php +++ b/app/modules/devices/list.php @@ -1,7 +1,7 @@ 0) { - $where[] = "d.device_type_id = ?"; - $types .= "i"; + $where[] = 'd.device_type_id = ?'; + $types .= 'i'; $params[] = $typeId; } if ($floorId > 0) { - $where[] = "f.id = ?"; - $types .= "i"; + $where[] = 'f.id = ?'; + $types .= 'i'; $params[] = $floorId; } if ($rackId > 0) { - $where[] = "d.rack_id = ?"; - $types .= "i"; + $where[] = 'd.rack_id = ?'; + $types .= 'i'; $params[] = $rackId; } $whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : ''; // ========================= -// Geräte laden +// Geraete laden // ========================= - $devices = $sql->get( - " - SELECT - d.id, - d.name, - d.serial_number, - d.rack_position_he, - d.rack_height_he, - d.web_config_url, - dt.name AS device_type, - dt.image_path, - f.name AS floor_name, - r.name AS rack_name, - ( - SELECT COUNT(*) - FROM device_ports dp - WHERE dp.device_id = d.id - ) AS port_count, - ( - SELECT COUNT(*) - FROM device_port_modules dpm - JOIN device_ports dp2 ON dp2.id = dpm.device_port_id - WHERE dp2.device_id = d.id - ) AS module_count, - ( - SELECT COUNT(*) - FROM connections c - WHERE (c.port_a_type = 'device' AND c.port_a_id IN ( - SELECT dp3.id FROM device_ports dp3 WHERE dp3.device_id = d.id - )) - OR (c.port_b_type = 'device' AND c.port_b_id IN ( - SELECT dp4.id FROM device_ports dp4 WHERE dp4.device_id = d.id - )) - ) AS connection_count +$devices = $sql->get( + " + SELECT + d.id, + d.name, + d.serial_number, + d.rack_position_he, + d.rack_height_he, + d.web_config_url, + dt.name AS device_type, + dt.image_path, + f.name AS floor_name, + r.name AS rack_name, + ( + SELECT COUNT(*) + FROM device_ports dp + WHERE dp.device_id = d.id + ) AS port_count, + ( + SELECT COUNT(*) + FROM device_ports dp + WHERE dp.device_id = d.id + AND dp.status = 'active' + ) AS active_port_count, + ( + SELECT COUNT(*) + FROM device_ports dp + WHERE dp.device_id = d.id + AND dp.status = 'disabled' + ) AS disabled_port_count, + ( + SELECT COUNT(*) + FROM device_ports dp + WHERE dp.device_id = d.id + AND dp.vlan_config IS NOT NULL + AND dp.vlan_config <> '[]' + ) AS vlan_port_count, + ( + SELECT COUNT(*) + FROM device_port_modules dpm + JOIN device_ports dp2 ON dp2.id = dpm.device_port_id + WHERE dp2.device_id = d.id + ) AS module_count, + ( + SELECT COUNT(*) + FROM connections c + WHERE (c.port_a_type = 'device' AND c.port_a_id IN ( + SELECT dp3.id FROM device_ports dp3 WHERE dp3.device_id = d.id + )) + OR (c.port_b_type = 'device' AND c.port_b_id IN ( + SELECT dp4.id FROM device_ports dp4 WHERE dp4.device_id = d.id + )) + ) AS connection_count FROM devices d JOIN device_types dt ON dt.id = d.device_type_id LEFT JOIN racks r ON r.id = d.rack_id @@ -99,22 +118,19 @@ $whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : ''; // ========================= // Filter-Daten laden // ========================= -$deviceTypes = $sql->get("SELECT id, name FROM device_types ORDER BY name", "", []); -$floors = $sql->get("SELECT id, name FROM floors ORDER BY name", "", []); -$racks = $sql->get("SELECT id, name FROM racks ORDER BY name", "", []); +$deviceTypes = $sql->get('SELECT id, name FROM device_types ORDER BY name', '', []); +$floors = $sql->get('SELECT id, name FROM floors ORDER BY name', '', []); +$racks = $sql->get('SELECT id, name FROM racks ORDER BY name', '', []); ?>
-

Geräte

+

Geraete

- -