Compare commits

...

2 Commits

Author SHA1 Message Date
4efd54613a connection edit bugfix 2026-02-16 09:53:35 +01:00
f14f92fdd8 links liste 2026-02-16 09:35:42 +01:00
4 changed files with 286 additions and 10 deletions

View File

@@ -0,0 +1,58 @@
(() => {
function applyTypeFilter(typeSelect, portSelect, selectedId) {
const endpointType = typeSelect.value;
let visibleCount = 0;
let matchedSelected = false;
const currentValue = selectedId || portSelect.value || '';
for (const option of portSelect.options) {
const optionType = option.dataset.endpointType || '';
if (!optionType) {
option.hidden = false;
option.disabled = false;
continue;
}
const visible = optionType === endpointType;
option.hidden = !visible;
option.disabled = !visible;
if (visible) {
visibleCount += 1;
if (option.value === currentValue) {
option.selected = true;
matchedSelected = true;
}
} else if (option.selected) {
option.selected = false;
}
}
const placeholder = portSelect.options[0];
if (placeholder) {
placeholder.textContent = visibleCount > 0 ? '- Port waehlen -' : '- Keine Ports verfuegbar -';
}
if (!matchedSelected) {
portSelect.value = '';
}
}
function bindPair(typeSelectId, portSelectId) {
const typeSelect = document.getElementById(typeSelectId);
const portSelect = document.getElementById(portSelectId);
if (!typeSelect || !portSelect) {
return;
}
applyTypeFilter(typeSelect, portSelect, portSelect.dataset.selectedId || '');
typeSelect.addEventListener('change', () => {
applyTypeFilter(typeSelect, portSelect, '');
});
}
document.addEventListener('DOMContentLoaded', () => {
bindPair('port_a_type', 'port_a_id');
bindPair('port_b_type', 'port_b_id');
});
})();

View File

@@ -0,0 +1,211 @@
<?php
/**
* app/modules/connections/edit.php
*
* Verbindung anlegen / bearbeiten
*/
$connectionId = (int)($_GET['id'] ?? 0);
$connection = null;
if ($connectionId > 0) {
$connection = $sql->single(
"SELECT id, port_a_type, port_a_id, port_b_type, port_b_id, vlan_config, comment
FROM connections
WHERE id = ?",
"i",
[$connectionId]
);
}
$normalizePortType = static function (string $value): string {
$map = [
'device' => 'device',
'device_ports' => 'device',
'module' => 'module',
'module_ports' => 'module',
'outlet' => 'outlet',
'network_outlet_ports' => 'outlet',
'patchpanel' => 'patchpanel',
'floor_patchpanel' => 'patchpanel',
'floor_patchpanel_ports' => 'patchpanel',
];
$key = strtolower(trim($value));
return $map[$key] ?? 'device';
};
$portAType = $normalizePortType((string)($connection['port_a_type'] ?? 'device'));
$portBType = $normalizePortType((string)($connection['port_b_type'] ?? 'device'));
$portAId = (int)($connection['port_a_id'] ?? 0);
$portBId = (int)($connection['port_b_id'] ?? 0);
$endpointOptions = [
'device' => [],
'module' => [],
'outlet' => [],
'patchpanel' => [],
];
$devicePorts = $sql->get(
"SELECT dp.id, dp.name, d.name AS owner_name
FROM device_ports dp
JOIN devices d ON d.id = dp.device_id
ORDER BY d.name, dp.name",
"",
[]
);
foreach ($devicePorts as $row) {
$endpointOptions['device'][] = [
'id' => (int)$row['id'],
'label' => $row['owner_name'] . ' / ' . $row['name'],
];
}
$modulePorts = $sql->get(
"SELECT
mp.id,
mp.name,
m.name AS module_name,
MIN(d.name) AS device_name
FROM module_ports mp
JOIN modules m ON m.id = mp.module_id
LEFT JOIN device_port_modules dpm ON dpm.module_id = m.id
LEFT JOIN device_ports dp ON dp.id = dpm.device_port_id
LEFT JOIN devices d ON d.id = dp.device_id
GROUP BY mp.id, mp.name, m.name
ORDER BY device_name, module_name, mp.name",
"",
[]
);
foreach ($modulePorts as $row) {
$deviceName = trim((string)($row['device_name'] ?? '')) ?: 'Unzugeordnet';
$endpointOptions['module'][] = [
'id' => (int)$row['id'],
'label' => $deviceName . ' / ' . $row['module_name'] . ' / ' . $row['name'],
];
}
$outletPorts = $sql->get(
"SELECT nop.id, nop.name, no.name AS outlet_name, r.name AS room_name, f.name AS floor_name
FROM network_outlet_ports nop
JOIN network_outlets no ON no.id = nop.outlet_id
LEFT JOIN rooms r ON r.id = no.room_id
LEFT JOIN floors f ON f.id = r.floor_id
ORDER BY floor_name, room_name, outlet_name, nop.name",
"",
[]
);
foreach ($outletPorts as $row) {
$parts = array_filter([(string)($row['floor_name'] ?? ''), (string)($row['room_name'] ?? ''), (string)$row['outlet_name'], (string)$row['name']]);
$endpointOptions['outlet'][] = [
'id' => (int)$row['id'],
'label' => implode(' / ', $parts),
];
}
$patchpanelPorts = $sql->get(
"SELECT fpp.id, fpp.name, fp.name AS patchpanel_name, f.name AS floor_name
FROM floor_patchpanel_ports fpp
JOIN floor_patchpanels fp ON fp.id = fpp.patchpanel_id
LEFT JOIN floors f ON f.id = fp.floor_id
ORDER BY floor_name, patchpanel_name, fpp.name",
"",
[]
);
foreach ($patchpanelPorts as $row) {
$parts = array_filter([(string)($row['floor_name'] ?? ''), (string)$row['patchpanel_name'], (string)$row['name']]);
$endpointOptions['patchpanel'][] = [
'id' => (int)$row['id'],
'label' => implode(' / ', $parts),
];
}
$vlanValue = '';
if (!empty($connection['vlan_config'])) {
$decoded = json_decode((string)$connection['vlan_config'], true);
if (is_array($decoded)) {
$vlanValue = implode(',', array_map('trim', $decoded));
} else {
$vlanValue = (string)$connection['vlan_config'];
}
}
$renderEndpointOptions = static function (array $optionsByType, int $selectedId): void {
foreach ($optionsByType as $type => $options) {
foreach ($options as $entry) {
$isSelected = ((int)$entry['id'] === $selectedId) ? ' selected' : '';
echo '<option value="' . (int)$entry['id'] . '" data-endpoint-type="' . htmlspecialchars((string)$type) . '"' . $isSelected . '>';
echo htmlspecialchars((string)$entry['label']);
echo '</option>';
}
}
};
?>
<div class="device-edit">
<h1><?php echo $connection ? 'Verbindung bearbeiten' : 'Neue Verbindung'; ?></h1>
<form method="post" action="?module=connections&action=save" class="edit-form">
<?php if ($connection): ?>
<input type="hidden" name="id" value="<?php echo (int)$connection['id']; ?>">
<?php endif; ?>
<fieldset>
<legend>Endpunkt A</legend>
<div class="form-group">
<label for="port_a_type">Typ</label>
<select id="port_a_type" name="port_a_type" required>
<option value="device" <?php echo $portAType === 'device' ? 'selected' : ''; ?>>Geraeteport</option>
<option value="module" <?php echo $portAType === 'module' ? 'selected' : ''; ?>>Modulport</option>
<option value="outlet" <?php echo $portAType === 'outlet' ? 'selected' : ''; ?>>Netzwerkdose</option>
<option value="patchpanel" <?php echo $portAType === 'patchpanel' ? 'selected' : ''; ?>>Patchpanel-Port</option>
</select>
</div>
<div class="form-group">
<label for="port_a_id">Port</label>
<select id="port_a_id" name="port_a_id" required data-selected-id="<?php echo (int)$portAId; ?>">
<option value="">- Port waehlen -</option>
<?php $renderEndpointOptions($endpointOptions, $portAId); ?>
</select>
</div>
</fieldset>
<fieldset>
<legend>Endpunkt B</legend>
<div class="form-group">
<label for="port_b_type">Typ</label>
<select id="port_b_type" name="port_b_type" required>
<option value="device" <?php echo $portBType === 'device' ? 'selected' : ''; ?>>Geraeteport</option>
<option value="module" <?php echo $portBType === 'module' ? 'selected' : ''; ?>>Modulport</option>
<option value="outlet" <?php echo $portBType === 'outlet' ? 'selected' : ''; ?>>Netzwerkdose</option>
<option value="patchpanel" <?php echo $portBType === 'patchpanel' ? 'selected' : ''; ?>>Patchpanel-Port</option>
</select>
</div>
<div class="form-group">
<label for="port_b_id">Port</label>
<select id="port_b_id" name="port_b_id" required data-selected-id="<?php echo (int)$portBId; ?>">
<option value="">- Port waehlen -</option>
<?php $renderEndpointOptions($endpointOptions, $portBId); ?>
</select>
</div>
</fieldset>
<fieldset>
<legend>Details</legend>
<div class="form-group">
<label for="vlan_config">VLANs (kommagetrennt)</label>
<input type="text" id="vlan_config" name="vlan_config" value="<?php echo htmlspecialchars($vlanValue); ?>" placeholder="z.B. 10,20,30">
</div>
<div class="form-group">
<label for="comment">Kommentar</label>
<textarea id="comment" name="comment" rows="3"><?php echo htmlspecialchars($connection['comment'] ?? ''); ?></textarea>
</div>
</fieldset>
<fieldset class="form-actions">
<button type="submit" class="button button-primary">Speichern</button>
<a href="?module=connections&action=list" class="button">Abbrechen</a>
</fieldset>
</form>
</div>
<script src="/assets/js/connections-edit-form.js" defer></script>

View File

@@ -29,11 +29,14 @@ $endpointUnionSql = "
'module' AS endpoint_type, 'module' AS endpoint_type,
mp.id AS endpoint_id, mp.id AS endpoint_id,
mp.name AS port_name, mp.name AS port_name,
CONCAT(d.name, ' / ', m.name) AS owner_name, CONCAT(IFNULL(MIN(d.name), 'Unzugeordnet'), ' / ', m.name) AS owner_name,
d.id AS owner_device_id MIN(d.id) AS owner_device_id
FROM module_ports mp FROM module_ports mp
JOIN modules m ON m.id = mp.module_id JOIN modules m ON m.id = mp.module_id
JOIN devices d ON d.id = m.device_id LEFT JOIN device_port_modules dpm ON dpm.module_id = m.id
LEFT JOIN device_ports dp ON dp.id = dpm.device_port_id
LEFT JOIN devices d ON d.id = dp.device_id
GROUP BY mp.id, mp.name, m.name
UNION ALL UNION ALL
SELECT SELECT
'outlet' AS endpoint_type, 'outlet' AS endpoint_type,
@@ -149,10 +152,12 @@ if ($deviceId > 0) {
FROM device_ports dp FROM device_ports dp
WHERE dp.device_id = ? WHERE dp.device_id = ?
UNION ALL UNION ALL
SELECT mp.id SELECT DISTINCT mp.id
FROM module_ports mp FROM module_ports mp
JOIN modules m ON m.id = mp.module_id JOIN modules m ON m.id = mp.module_id
WHERE m.device_id = ? JOIN device_port_modules dpm ON dpm.module_id = m.id
JOIN device_ports dp ON dp.id = dpm.device_port_id
WHERE dp.device_id = ?
) p", ) p",
"ii", "ii",
[$deviceId, $deviceId] [$deviceId, $deviceId]
@@ -193,10 +198,12 @@ if ($deviceId > 0) {
FROM device_ports dp FROM device_ports dp
WHERE dp.device_id = ? WHERE dp.device_id = ?
UNION ALL UNION ALL
SELECT CONCAT(m.name, ' / ', mp.name) AS name, NULL AS vlan_config, (1000000 + mp.id) AS sort_id SELECT DISTINCT CONCAT(m.name, ' / ', mp.name) AS name, NULL AS vlan_config, (1000000 + mp.id) AS sort_id
FROM module_ports mp FROM module_ports mp
JOIN modules m ON m.id = mp.module_id JOIN modules m ON m.id = mp.module_id
WHERE m.device_id = ? JOIN device_port_modules dpm ON dpm.module_id = m.id
JOIN device_ports dp ON dp.id = dpm.device_port_id
WHERE dp.device_id = ?
) p ) p
ORDER BY sort_id ORDER BY sort_id
LIMIT 12", LIMIT 12",
@@ -251,7 +258,7 @@ if ($deviceId > 0) {
<button type="submit" class="button">Filter</button> <button type="submit" class="button">Filter</button>
<a href="?module=connections&action=list" class="button">Reset</a> <a href="?module=connections&action=list" class="button">Reset</a>
<a href="?module=connections&action=save" class="button button-primary">+ Neue Verbindung</a> <a href="?module=connections&action=edit" class="button button-primary">+ Neue Verbindung</a>
</form> </form>
</div> </div>
@@ -337,7 +344,7 @@ if ($deviceId > 0) {
<div class="empty-state"> <div class="empty-state">
<p>Keine Verbindungen gefunden.</p> <p>Keine Verbindungen gefunden.</p>
<p> <p>
<a href="?module=connections&action=save" class="button button-primary"> <a href="?module=connections&action=edit" class="button button-primary">
Erste Verbindung anlegen Erste Verbindung anlegen
</a> </a>
</p> </p>

View File

@@ -53,7 +53,7 @@ if ($portAId <= 0 || $portBId <= 0) {
if (!empty($errors)) { if (!empty($errors)) {
$_SESSION['error'] = implode(', ', $errors); $_SESSION['error'] = implode(', ', $errors);
$redirectUrl = $connId ? "?module=connections&action=edit&id=$connId" : "?module=connections&action=list"; $redirectUrl = $connId ? "?module=connections&action=edit&id=$connId" : "?module=connections&action=edit";
header("Location: $redirectUrl"); header("Location: $redirectUrl");
exit; exit;
} }