diff --git a/NEXT.md b/NEXT.md index f0ed0d8..ef33ab9 100644 --- a/NEXT.md +++ b/NEXT.md @@ -1,14 +1,10 @@ # NEXT_STEPS ## Aktive Aufgaben (priorisiert) -- [ ] [#11] Encoding- und Umlautfehler bereinigen (inkl. Anzeige in UI-Dateien und Markdown-Dokumenten) ## Verifikation (Status unklar, nicht als erledigt markieren ohne Reproduktion + Commit) - [ ] [#15] Neue Verbindung: Netzwerkdose auswählbar (Regressionstest in UI durchführen) ## gefundene bugs -- [ ] device löschen geht nicht - [ ] TODO Design vereinheitlichen - -- [ ] Validierungsregeln fuer Topologie fest verdrahten (Patchpanel-Port nur mit Patchpanel-Port oder Netzwerkbuchsen-Port). 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/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/modules/connections/save.php b/app/modules/connections/save.php index 8f0a51d..cd001c4 100644 --- a/app/modules/connections/save.php +++ b/app/modules/connections/save.php @@ -42,6 +42,18 @@ $normalizePortType = static function (string $value): string { $portAType = $normalizePortType((string)$portAType); $portBType = $normalizePortType((string)$portBType); +$isTopologyPairAllowed = static function (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; +}; + // ========================= // Validierung (einfach) // ========================= @@ -50,6 +62,9 @@ $errors = []; if ($portAId <= 0 || $portBId <= 0) { $errors[] = "Beide Ports sind erforderlich"; } +if (!$isTopologyPairAllowed($portAType, $portBType)) { + $errors[] = "Patchpanel-Ports duerfen nur mit Patchpanel-Ports oder Netzwerkdosen-Ports verbunden werden"; +} $otherConnections = $sql->get( "SELECT id, port_a_type, port_a_id, port_b_type, port_b_id diff --git a/app/modules/devices/delete.php b/app/modules/devices/delete.php index a4e66da..06fc9ed 100644 --- a/app/modules/devices/delete.php +++ b/app/modules/devices/delete.php @@ -56,10 +56,10 @@ $dependencies = $sql->single( ( SELECT COUNT(*) FROM connections c - WHERE (c.port_a_type = 'device' AND c.port_a_id IN ( + WHERE ((c.port_a_type = 'device' OR c.port_a_type = 'device_ports') AND c.port_a_id IN ( SELECT dp3.id FROM device_ports dp3 WHERE dp3.device_id = ? )) - OR (c.port_b_type = 'device' AND c.port_b_id IN ( + OR ((c.port_b_type = 'device' OR c.port_b_type = 'device_ports') AND c.port_b_id IN ( SELECT dp4.id FROM device_ports dp4 WHERE dp4.device_id = ? )) ) AS connection_count", @@ -108,8 +108,8 @@ if ($hasDependencies && !$forceDelete) { // Connections referenzieren device_ports nur logisch, daher manuell entfernen. $sql->set( "DELETE FROM connections - WHERE (port_a_type = 'device' AND port_a_id IN (SELECT id FROM device_ports WHERE device_id = ?)) - OR (port_b_type = 'device' AND port_b_id IN (SELECT id FROM device_ports WHERE device_id = ?))", + WHERE ((port_a_type = 'device' OR port_a_type = 'device_ports') AND port_a_id IN (SELECT id FROM device_ports WHERE device_id = ?)) + OR ((port_b_type = 'device' OR port_b_type = 'device_ports') AND port_b_id IN (SELECT id FROM device_ports WHERE device_id = ?))", "ii", [$deviceId, $deviceId] ); diff --git a/app/modules/devices/edit.php b/app/modules/devices/edit.php index 72d87cf..5fd3cbd 100644 --- a/app/modules/devices/edit.php +++ b/app/modules/devices/edit.php @@ -47,10 +47,10 @@ if ($isEdit) { ( SELECT COUNT(*) FROM connections c - WHERE (c.port_a_type = 'device' AND c.port_a_id IN ( + WHERE ((c.port_a_type = 'device' OR c.port_a_type = 'device_ports') AND c.port_a_id IN ( SELECT dp3.id FROM device_ports dp3 WHERE dp3.device_id = ? )) - OR (c.port_b_type = 'device' AND c.port_b_id IN ( + OR ((c.port_b_type = 'device' OR c.port_b_type = 'device_ports') AND c.port_b_id IN ( SELECT dp4.id FROM device_ports dp4 WHERE dp4.device_id = ? )) ) AS connection_count", diff --git a/app/modules/devices/list.php b/app/modules/devices/list.php index c41a448..eb02118 100644 --- a/app/modules/devices/list.php +++ b/app/modules/devices/list.php @@ -97,10 +97,10 @@ $devices = $sql->get( ( SELECT COUNT(*) FROM connections c - WHERE (c.port_a_type = 'device' AND c.port_a_id IN ( + WHERE ((c.port_a_type = 'device' OR c.port_a_type = 'device_ports') 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 ( + OR ((c.port_b_type = 'device' OR c.port_b_type = 'device_ports') AND c.port_b_id IN ( SELECT dp4.id FROM device_ports dp4 WHERE dp4.device_id = d.id )) ) AS connection_count