feat: complete package 2 delete flows and package 3 port management

This commit is contained in:
2026-02-18 10:16:24 +01:00
parent f4ce7f360d
commit 77758f71d3
14 changed files with 754 additions and 141 deletions

View File

@@ -1,7 +1,7 @@
<?php
/**
* modules/devices/list.php
* Vollständige Geräteübersicht mit Filter
* Vollstaendige Geraeteuebersicht mit Filter
*/
// =========================
@@ -22,69 +22,88 @@ $params = [];
if ($search !== '') {
$where[] = "(d.name LIKE ? OR d.serial_number LIKE ? OR dt.name LIKE ?)";
$types .= "sss";
$types .= 'sss';
$params[] = "%$search%";
$params[] = "%$search%";
$params[] = "%$search%";
}
if ($typeId > 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', '', []);
?>
<div class="devices-container">
<h1>Geräte</h1>
<h1>Geraete</h1>
<!-- =========================
Filter-Toolbar
========================= -->
<form method="get" class="filter-form">
<input type="hidden" name="module" value="devices">
<input type="hidden" name="action" value="list">
<input type="text" name="search" placeholder="Suche nach Name oder Seriennummer"
<input type="text" name="search" placeholder="Suche nach Name oder Seriennummer..."
value="<?php echo htmlspecialchars($search); ?>" class="search-input">
<select name="type_id">
@@ -149,16 +165,13 @@ $racks = $sql->get("SELECT id, name FROM racks ORDER BY name", "", []);
<a href="?module=devices&action=list" class="button">Reset</a>
<a href="?module=devices&action=edit" class="button button-primary" style="margin-left: auto;">
+ Neues Gerät
+ Neues Geraet
</a>
</form>
<!-- =========================
Geräte-Liste
========================= -->
<?php if (!empty($devices)): ?>
<div class="device-stats">
<p>Gefundene Geräte: <strong><?php echo count($devices); ?></strong></p>
<p>Gefundene Geraete: <strong><?php echo count($devices); ?></strong></p>
</div>
<table class="device-list">
@@ -169,6 +182,7 @@ $racks = $sql->get("SELECT id, name FROM racks ORDER BY name", "", []);
<th>Stockwerk</th>
<th>Rack</th>
<th>Position (HE)</th>
<th>Ports</th>
<th>Seriennummer</th>
<th>Webconfig</th>
<th>Aktionen</th>
@@ -186,28 +200,37 @@ $racks = $sql->get("SELECT id, name FROM racks ORDER BY name", "", []);
</td>
<td>
<?php echo htmlspecialchars($d['floor_name'] ?? ''); ?>
<?php echo htmlspecialchars($d['floor_name'] ?? '-'); ?>
</td>
<td>
<?php echo htmlspecialchars($d['rack_name'] ?? ''); ?>
<?php echo htmlspecialchars($d['rack_name'] ?? '-'); ?>
</td>
<td>
<?php
if ($d['rack_position_he']) {
echo $d['rack_position_he'];
echo (int)$d['rack_position_he'];
if ($d['rack_height_he']) {
echo "" . ($d['rack_position_he'] + $d['rack_height_he'] - 1);
echo '-' . ((int)$d['rack_position_he'] + (int)$d['rack_height_he'] - 1);
}
} else {
echo "—";
echo '-';
}
?>
</td>
<td>
<small><?php echo htmlspecialchars($d['serial_number'] ?? '—'); ?></small>
<small>
<?php echo (int)$d['port_count']; ?> gesamt<br>
<?php echo (int)$d['active_port_count']; ?> aktiv /
<?php echo (int)$d['disabled_port_count']; ?> inaktiv<br>
VLAN gesetzt: <?php echo (int)$d['vlan_port_count']; ?>
</small>
</td>
<td>
<small><?php echo htmlspecialchars($d['serial_number'] ?? '-'); ?></small>
</td>
<td>
@@ -216,13 +239,13 @@ $racks = $sql->get("SELECT id, name FROM racks ORDER BY name", "", []);
Webconfig
</a>
<?php else: ?>
-
<?php endif; ?>
</td>
<td class="actions">
<a href="?module=devices&action=edit&id=<?php echo $d['id']; ?>" class="button button-small">Bearbeiten</a>
<a href="?module=devices&action=delete&id=<?php echo (int)$d['id']; ?>" class="button button-small button-danger" onclick="return confirmDelete(this, <?php echo (int)$d['id']; ?>, <?php echo (int)$d['connection_count']; ?>, <?php echo (int)$d['port_count']; ?>, <?php echo (int)$d['module_count']; ?>)">Löschen</a>
<a href="?module=devices&action=delete&id=<?php echo (int)$d['id']; ?>" class="button button-small button-danger" onclick="return confirmDelete(this, <?php echo (int)$d['id']; ?>, <?php echo (int)$d['connection_count']; ?>, <?php echo (int)$d['port_count']; ?>, <?php echo (int)$d['module_count']; ?>)">Loeschen</a>
</td>
</tr>
<?php endforeach; ?>
@@ -231,10 +254,10 @@ $racks = $sql->get("SELECT id, name FROM racks ORDER BY name", "", []);
<?php else: ?>
<div class="empty-state">
<p>Keine Geräte gefunden.</p>
<p>Keine Geraete gefunden.</p>
<p>
<a href="?module=devices&action=edit" class="button button-primary">
Erstes Gerät anlegen
Erstes Geraet anlegen
</a>
</p>
</div>
@@ -351,33 +374,41 @@ $racks = $sql->get("SELECT id, name FROM racks ORDER BY name", "", []);
<script>
function confirmDelete(link, id, connectionCount, portCount, moduleCount) {
if (confirm('Dieses Gerät wirklich löschen?')) {
const hasDependencies = (connectionCount > 0) || (portCount > 0) || (moduleCount > 0);
if (hasDependencies) {
const details = [];
if (connectionCount > 0) {
details.push(connectionCount + ' Verbindungen');
}
if (portCount > 0) {
details.push(portCount + ' Ports');
}
if (moduleCount > 0) {
details.push(moduleCount + ' Port-Module');
}
const dependencyMessage = 'Es gibt abhängige Daten (' + details.join(', ') + '). Diese auch löschen?';
if (!confirm(dependencyMessage)) {
return false;
}
window.location.href = (link && link.href ? link.href : ('?module=devices&action=delete&id=' + encodeURIComponent(id))) + '&force=1';
return false;
}
return true;
if (!confirm('Dieses Geraet wirklich loeschen?')) {
return false;
}
const requestDelete = (forceDelete) => {
const body = ['id=' + encodeURIComponent(id)];
if (forceDelete) {
body.push('force=1');
}
fetch('?module=devices&action=delete', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: body.join('&')
})
.then((response) => response.json())
.then((data) => {
if (data && data.success) {
window.location.reload();
return;
}
if (data && data.requires_force) {
if (confirm(data.message || 'Es gibt abhaengige Daten. Trotzdem loeschen?')) {
requestDelete(true);
}
return;
}
alert((data && data.message) ? data.message : 'Loeschen fehlgeschlagen');
})
.catch(() => alert('Loeschen fehlgeschlagen'));
};
requestDelete(false);
return false;
}
</script>