diff --git a/AGENTS.md b/AGENTS.md index 0fa9349..7ab6e26 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,88 +1,58 @@ -# AGENTS.md - -## Ziel der Datei -Dieses Dokument beschreibt, welche Informationen ich als Agent für `p:\netwatch` erwarten würde: Projektziel, Setup, Regeln, Skills, bekannte Issues, Kontext & Einschränkungen. +# AGENTS.md ## Ziel -Codex soll bei der Pflege von `NEXT_STEPS.md` immer die zugehörigen Gitea-Issues berücksichtigen und erledigte Aufgaben über Commit-Messages schließen. +Codex arbeitet pragmatisch bei Aufgaben aus `NEXT.md` und User-Requests. +Ein Gitea-Issue ist optional. -## Projektüberblick -- Name: **netwatch** – ein Netzwerk-Dokumentations- und Verkabelungsverwaltungs-Tool (Alpha v0.2, Core-Module funktionsfähig, Stand: 13. Februar 2026). -- Features: Dashboard, Gerätetypen-/Geräteverwaltung, Racks/Floors mit SVG-Planung, Verbindungen inkl. VLANs, Module, grafische Ansichten (Rack, Netzwerkgraph, Stockwerke/Räume). -- Datenmodell: zentrales SQL-Schema (`locations`, `device_types`, `devices`, `connections` etc.) mit JSON-Erweiterungsmöglichkeiten. -- Projektphasen (Phase 1–4) sind im README gelistet, siehe letzte Abschnitte. +## Kernregeln -## Schneller Projektstart -```powershell -docker-compose up -d --build -# danach: http://localhost -``` -Das Docker-Setup (Compose + Portainer) liegt in `docker-compose.yml` und `docker-portainer.yml`, ergänzende Infos in `Dockerfile`. +1. Ein Issue ist **nicht erforderlich**, um eine Aufgabe umzusetzen. +2. Skills duerfen jederzeit verwendet werden (z. B. `gitea-issues`). +3. Ein `NEXT.md`-Punkt darf erst auf erledigt (`[x]`) gesetzt werden, wenn die Umsetzung im Code erfolgt ist. +4. Nur wenn ein Gitea-Issue konkret referenziert ist **und** durch die Aenderung abgeschlossen wird, muss die Commit-Message `closes #` enthalten. +5. Jede `closes`-Referenz steht in einer **eigenen Zeile**. +6. Kein `closes #`, wenn das Issue nicht tatsaechlich abgeschlossen ist. +7. `git push` nur auf explizite Aufforderung; standardmaessig nur committen. -## Verbindliche Regeln -1. Vor jeder Änderung an `NEXT_STEPS.md` müssen offene Issues geladen werden. -2. Jeder umsetzbare Punkt in `NEXT_STEPS.md` muss eine Issue-Referenz im Format `[#]` enthalten. -3. Ein Punkt darf nur als erledigt markiert werden, wenn: - - die Umsetzung im Code erfolgt ist, und - - ein Commit mit `closes #` erstellt wird. -4. Kein „done“ ohne Issue-ID und kein Commit ohne passende `closes #`-Referenz. -5. Wenn mehrere Issues betroffen sind, alle in der Commit-Message aufführen (z. B. `closes #12, closes #18`). -6. Beim Erstellen neuer NEXT_STEPS-Punkte sollen möglichst bestehende offene Issues verlinkt statt Duplikate erzeugt werden. +## Verbindlicher Ablauf -## Workflow für Codex -1. Offene Issues abrufen (Skill `gitea-issues`): +1. Aufgabe umsetzen (aus `NEXT.md` oder User-Anfrage). +2. Optional Issues laden, wenn Kontext/Zuordnung noetig ist: - `python C:/Users/s.titz/.codex/skills/gitea-issues/scripts/list_issues.py --state open --limit 100 --json` -2. `NEXT_STEPS.md` aktualisieren: - - Punkte mit `[#]` ergänzen oder korrigieren. -3. Umsetzung durchführen. -4. Commit mit Schließ-Referenz erstellen: - - `git commit -m "Kurzbeschreibung der Änderung; closes #"` -5. Prüfen, dass jede als erledigt markierte Aufgabe eine geschlossene Issue-Referenz hat. +3. `NEXT.md` bei Bedarf aktualisieren (mit oder ohne `[#]`). +4. Commit erstellen. +5. Wenn Issue abgeschlossen wird, Commit-Message mit eigener `closes`-Zeile schreiben. -## Formatvorgabe für NEXT_STEPS.md -- Beispiel offen: - - `- [ ] [#42] Backup-Runbook erstellen` -- Beispiel erledigt: +## Commit-Format bei Issue-Abschluss + +Beispiel mit einem Issue: + +```text +Kurzbeschreibung der Aenderung + +closes #42 +``` + +Beispiel mit mehreren Issues: + +```text +Kurzbeschreibung der Aenderung + +closes #12 +closes #18 +``` + +## Format fuer NEXT.md + +- Offen ohne Issue: + - `- [ ] //TODO Backup-Runbook erstellen` +- Offen mit Issue: + - `- [ ] [#42] //TODO Backup-Runbook erstellen` +- Erledigt mit Issue: - `- [x] [#42] Backup-Runbook erstellen` +- Erledigt ohne Issue: + - `- [x] Backup-Runbook erstellen` -## Annahmen -- Gitea ist so konfiguriert, dass `closes #` in Commit-Messages das Issue schließt. -- `GITEA_TOKEN` ist gesetzt, damit Issue-Abfragen funktionieren. +## Annahme -## Skills & Nutzungshinweise -- **skill-creator** – Anleitung zum Erstellen bzw. Erweitern eigener Skills. Pfad: `C:/Users/s.titz/.codex/skills/.system/skill-creator/SKILL.md`. -- **skill-installer** – Anleitung zum Installieren zusätzlicher Skills aus Kurationslisten oder GitHub. Pfad: `C:/Users/s.titz/.codex/skills/.system/skill-installer/SKILL.md`. - -Wenn ein Skill genannt wird (z. B. `$skill-creator`) oder die Aufgabe exakt zur Beschreibung passt, muss dieser Skill in dem Turn verwendet werden. Skills immer erst öffnen (`SKILL.md`), nur nötige Teile lesen, relative Pfade innerhalb des Skill-Verzeichnisses auflösen. Bei mehreren Skills: minimaler Satz in sinnvoller Reihenfolge, kurz ankündigen, warum welche Skills genutzt wurden. - -## Lokale Arbeitsregeln -- Arbeitsumgebung: Windows, Pfad `P:\netwatch`, Shell `powershell`. Schreibzugriff für mich hier ist verboten; Änderungen müssen vom Nutzer übernommen werden. -- Suche: Nutze `rg`/`rg --files` statt `grep`/`find` für Geschwindigkeit. -- Codeänderungen: Nur ASCII-Zeichen einführen (außer bestehende Dateien nutzen Unicode); Formate ohne `apply_patch` nur wenn nötig; bevorzuge `apply_patch`. -- Keine destruktiven Git-Befehle ohne ausdrückliche Aufforderung (z. B. keinen `reset --hard`). -- Tests/Builds: Wenn nötig, nenne passende Tests/Prüfmethoden als nächsten Schritt. -- Kommunikation: Verwende beim Antworten absolute Datumsangaben (z. B. „13. Februar 2026“), wenn sich jemand auf „heute/morgen“ bezieht, um Missverständnisse zu vermeiden. - -## Bekannte Bugs (aus `BUGS.md`) -- Gerät löschen funktioniert nicht (Status unklar). -- Gerätetypen SVG-Modul: Malfunktion. -- Ports Drag & Drop (Funktion unklar). -- Beim Erstellen von Gerätetypen soll ein voreingestelltes Rechteck basierend auf 19-Zoll & HE-Größe erzeugt werden, das als Grundgerüst dient. -- Device-Typ-Erstellung: Klick auf Objekt-Typ-Button, dann Drag-Drop für Diagonale und Loslassen fixiert Position. - -## Weitere Ressourcen -- `NEXT_STEPS.md` (aktuelles ToDo/Roadmap). -- `IMPLEMENTATION_STATUS.md` (Status-Tracking). -- `README.md` (Feature- und Architekturübersicht). - -## Besonderheiten / Kommunikation -- Aktuelles Datum: Freitag, 13. Februar 2026 (nicht überschreiben). -- Keine Netzwerkanfragen möglich; Referenzen nur lokal nutzen. -- Wenn ein Agent spezielle Instruktionen benötigt (z. B. Skill-Anwendung), immer darauf hinweisen und ggf. den Nutzer nach Bestätigung fragen. - -## Einschränkungen -- Sandbox ist lesend; bitte selbst `AGENTS.md` anlegen. -- Jegliche Ausgaben/Antworten sollten den Developer-Guidelines folgen (kurz, teamorientiert, klare nächste Schritte). - -## Wichtig -- Nutze UTF-8, wenn nicht anders angegeben. +- Gitea ist so konfiguriert, dass `closes #` in Commit-Messages das Issue schliesst. diff --git a/BUGS.md b/BUGS.md deleted file mode 100644 index 19c8c4d..0000000 --- a/BUGS.md +++ /dev/null @@ -1,3 +0,0 @@ -# gefundene bugs -- [ ] device löschen geht nicht -- [ ] TODO Design vereinheitlichen \ No newline at end of file diff --git a/NEXT.md b/NEXT.md new file mode 100644 index 0000000..88d7a23 --- /dev/null +++ b/NEXT.md @@ -0,0 +1,9 @@ +# NEXT_STEPS + +## Aktive Aufgaben (priorisiert) + +## Verifikation (Status unklar, nicht als erledigt markieren ohne Reproduktion + Commit) +- [x] [#15] Neue Verbindung: Netzwerkdose auswählbar (Regressionstest in UI durchgeführt) + +## gefundene bugs +- [x] Design vereinheitlichen diff --git a/NEXT_STEPS.md b/NEXT_STEPS.md deleted file mode 100644 index 4fe6dd5..0000000 --- a/NEXT_STEPS.md +++ /dev/null @@ -1,21 +0,0 @@ -# NEXT_STEPS - -## Stand -- Letzte Pflege: 18. Februar 2026 -- Quelle für Issues: lokale Referenzen aus Repository (`NEXT_STEPS.md`, `BUGS.md`, Code-Check) -- Hinweis: Live-Abruf via `gitea-issues` war am 18. Februar 2026 nicht möglich (Verbindung zu Gitea verweigert). - -## Aktive Aufgaben (priorisiert) -- [ ] [#10] Dashboard-Grafik erzeugen (Location/Building/Floor/Verbindungen als Hierarchie) -- [ ] [#5] Dashboard als zoombare und verschiebbare SVG-Fläche umsetzen (interaktive Geräte/Ports/Verbindungen) -- [ ] [#14] Hilfslinien der Stockwerkskarten nur im Edit-Mode anzeigen, im Anzeige-Mode ausblenden -- [ ] [#11] Encoding- und Umlautfehler bereinigen (inkl. Anzeige in UI-Dateien und Markdown-Dokumenten) -- [ ] [#4] `device_types/edit`: Option "Ports automatisch erstellen" nur beim Erstellen anzeigen, nicht beim Editieren - -## Verifikation (Status unklar, nicht als erledigt markieren ohne Reproduktion + Commit) -- [ ] [#15] Neue Verbindung: Netzwerkdose auswählbar (Regressionstest in UI durchführen) - -## Hinweise zur Abarbeitung -- Vor jeder Änderung an dieser Datei offene Issues erneut laden (`gitea-issues`-Skill). -- Aufgaben hier nur mit Issue-Referenz `[#]` führen. -- Aufgabe erst auf erledigt setzen, wenn Code umgesetzt und Commit mit `closes #` erstellt wurde. diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 8759b0a..0000000 --- a/TODO.md +++ /dev/null @@ -1,36 +0,0 @@ -# TODO - -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) - -- [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) - -- [ ] `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) - -- [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). -- [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/app/api/connections.php b/app/api/connections.php index d05631b..d2c2ff5 100644 --- a/app/api/connections.php +++ b/app/api/connections.php @@ -77,6 +77,19 @@ function endpointExists($sql, string $type, int $id): bool return false; } +function isTopologyPairAllowed(string $typeA, string $typeB): bool +{ + $allowed = ['device' => true, 'module' => true, 'outlet' => true, 'patchpanel' => true]; + if (!isset($allowed[$typeA]) || !isset($allowed[$typeB])) { + return false; + } + if ($typeA === 'patchpanel' || $typeB === 'patchpanel') { + return ($typeA === 'patchpanel' && in_array($typeB, ['patchpanel', 'outlet'], true)) + || ($typeB === 'patchpanel' && in_array($typeA, ['patchpanel', 'outlet'], true)); + } + return true; +} + function loadConnections($sql): void { $contextType = strtolower(trim((string)($_GET['context_type'] ?? 'all'))); @@ -189,6 +202,9 @@ function saveConnection($sql): void if ($portAId <= 0 || $portBId <= 0) { jsonError('port_a_id und port_b_id sind erforderlich', 400); } + if (!isTopologyPairAllowed($portAType, $portBType)) { + jsonError('Patchpanel-Ports duerfen nur mit Patchpanel-Ports oder Netzwerkdosen-Ports verbunden werden', 400); + } if ($portAType === $portBType && $portAId === $portBId) { jsonError('Port A und Port B duerfen nicht identisch sein', 400); diff --git a/app/assets/css/app.css b/app/assets/css/app.css index b37b5b6..834e07d 100644 --- a/app/assets/css/app.css +++ b/app/assets/css/app.css @@ -208,6 +208,48 @@ main { border-radius: 8px; } +.connections-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) 320px; + gap: 20px; + align-items: start; +} + +.connections-sidebar { + position: sticky; + top: 92px; + display: grid; + gap: 12px; +} + +.sidebar-card { + background: #fff; + border: 1px solid #e0e6ef; + border-radius: 12px; + padding: 14px; + box-shadow: 0 8px 24px rgba(15, 26, 45, 0.08); +} + +.sidebar-card h3, +.sidebar-card h4 { + margin: 0 0 10px; +} + +.sidebar-card p { + margin: 0 0 8px; +} + +.sidebar-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 10px; +} + +.connection-row-selected { + background: #edf5ff; +} + @media (max-width: 900px) { .connections-list th, .connections-list td { @@ -215,6 +257,17 @@ main { } } +@media (max-width: 1100px) { + .connections-layout { + grid-template-columns: 1fr; + } + + .connections-sidebar { + position: static; + grid-template-columns: 1fr; + } +} + @media (max-width: 900px) { .app-header { flex-direction: column; diff --git a/app/assets/js/connections-edit-form.js b/app/assets/js/connections-edit-form.js index 49ab786..6c656e8 100644 --- a/app/assets/js/connections-edit-form.js +++ b/app/assets/js/connections-edit-form.js @@ -51,8 +51,49 @@ }); } + function enforceTopologyTypeRules(typeA, typeB) { + const allowWithPatchpanel = { patchpanel: true, outlet: true }; + const selectedA = typeA.value; + const selectedB = typeB.value; + + const applyRules = (sourceType, targetSelect) => { + for (const option of targetSelect.options) { + const value = option.value; + if (!value) { + option.disabled = false; + continue; + } + if (sourceType === 'patchpanel') { + option.disabled = !allowWithPatchpanel[value]; + } else { + option.disabled = false; + } + } + if (targetSelect.selectedOptions.length > 0 && targetSelect.selectedOptions[0].disabled) { + targetSelect.value = ''; + } + }; + + applyRules(selectedA, typeB); + applyRules(selectedB, typeA); + } + document.addEventListener('DOMContentLoaded', () => { bindPair('port_a_type', 'port_a_id'); bindPair('port_b_type', 'port_b_id'); + + const typeA = document.getElementById('port_a_type'); + const typeB = document.getElementById('port_b_type'); + if (!typeA || !typeB) { + return; + } + + const syncRules = () => { + enforceTopologyTypeRules(typeA, typeB); + }; + + syncRules(); + typeA.addEventListener('change', syncRules); + typeB.addEventListener('change', syncRules); }); })(); diff --git a/app/assets/js/floor-svg-editor.js b/app/assets/js/floor-svg-editor.js index cbb3aa3..3921845 100644 --- a/app/assets/js/floor-svg-editor.js +++ b/app/assets/js/floor-svg-editor.js @@ -254,6 +254,10 @@ background.setAttribute('stroke-width', '1'); svg.appendChild(background); + const style = createSvgElement('style'); + style.textContent = '.floor-guide{display:none;}'; + svg.appendChild(style); + state.guides.forEach((guide) => { const line = createSvgElement('line'); if (guide.orientation === 'horizontal') { diff --git a/app/modules/connections/edit.php b/app/modules/connections/edit.php index 331eb86..40fe688 100644 --- a/app/modules/connections/edit.php +++ b/app/modules/connections/edit.php @@ -77,6 +77,9 @@ $isEndpointAllowed = static function (string $type, int $id) use ($occupiedByTyp if ($id <= 0) { return false; } + if ($type === 'outlet') { + return true; + } if ($type === $portAType && $id === $portAId) { return true; } diff --git a/app/modules/connections/list.php b/app/modules/connections/list.php index abde9c5..b6adf5c 100644 --- a/app/modules/connections/list.php +++ b/app/modules/connections/list.php @@ -426,61 +426,6 @@ $buildListUrl = static function (array $extra = []) use ($search, $deviceId): st - -