Files
netwatch/app/modules/devices/edit.php

405 lines
14 KiB
PHP

<?php
/**
* app/modules/devices/edit.php
*
* Konkretes Gerät anlegen / bearbeiten
* - Name, Seriennummer
* - Gerätetyp wählen
* - Standort (Rack, HE-Position)
*/
// =========================
// Kontext bestimmen
// =========================
$deviceId = (int)($_GET['id'] ?? 0);
$device = null;
if ($deviceId > 0) {
$device = $sql->single(
"SELECT d.* FROM devices d WHERE d.id = ?",
"i",
[$deviceId]
);
}
$isEdit = !empty($device);
$pageTitle = $isEdit ? "Gerät bearbeiten: " . htmlspecialchars($device['name']) : "Neues Gerät";
$dependencyCounts = [
'port_count' => 0,
'module_count' => 0,
'connection_count' => 0
];
if ($isEdit) {
$dependencyCounts = $sql->single(
"SELECT
(
SELECT COUNT(*)
FROM device_ports dp
WHERE dp.device_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 = ?
) 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 = ?
))
OR (c.port_b_type = 'device' AND c.port_b_id IN (
SELECT dp4.id FROM device_ports dp4 WHERE dp4.device_id = ?
))
) AS connection_count",
"iiii",
[$deviceId, $deviceId, $deviceId, $deviceId]
) ?: $dependencyCounts;
}
// =========================
// Optionen laden
// =========================
$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]
);
}
?>
<div class="device-edit">
<h1><?php echo $pageTitle; ?></h1>
<form method="post" action="?module=devices&action=save" class="edit-form">
<?php if ($isEdit): ?>
<input type="hidden" name="id" value="<?php echo $deviceId; ?>">
<?php endif; ?>
<!-- =========================
Basisdaten
========================= -->
<fieldset>
<legend>Allgemein</legend>
<div class="form-group">
<label for="name">Name <span class="required">*</span></label>
<input type="text" id="name" name="name" required
value="<?php echo htmlspecialchars($device['name'] ?? ''); ?>"
placeholder="z.B. Core Switch 1">
</div>
<div class="form-group">
<label for="device_type_id">Gerätetyp <span class="required">*</span></label>
<select id="device_type_id" name="device_type_id" required>
<option value="">- Wählen -</option>
<?php foreach ($deviceTypes as $type): ?>
<option value="<?php echo $type['id']; ?>"
<?php echo ($device['device_type_id'] ?? 0) == $type['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($type['name']); ?>
<small>(<?php echo $type['category']; ?>)</small>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="serial_number">Seriennummer</label>
<input type="text" id="serial_number" name="serial_number"
value="<?php echo htmlspecialchars($device['serial_number'] ?? ''); ?>"
placeholder="Optionales Feld">
</div>
<div class="form-group">
<label for="web_config_url">Webconfig-URL</label>
<input type="url" id="web_config_url" name="web_config_url"
value="<?php echo htmlspecialchars($device['web_config_url'] ?? ''); ?>"
placeholder="https://switch.example.local">
<small>Optionaler Link zur Weboberfläche des Geräts (öffnet in neuem Tab).</small>
</div>
<div class="form-group">
<label for="comment">Kommentar</label>
<textarea id="comment" name="comment" rows="3"
placeholder="Notizen zu diesem Gerät"><?php echo htmlspecialchars($device['comment'] ?? ''); ?></textarea>
</div>
</fieldset>
<!-- =========================
Standort im Rack
========================= -->
<fieldset>
<legend>Standort</legend>
<div class="form-group">
<label for="rack_id">Rack <span class="required">*</span></label>
<select id="rack_id" name="rack_id" required>
<option value="">- Wählen -</option>
<?php foreach ($racks as $rack): ?>
<option value="<?php echo $rack['id']; ?>"
<?php echo ($device['rack_id'] ?? 0) == $rack['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($rack['name']); ?>
</option>
<?php endforeach; ?>
</select>
<small>Wählen Sie das Rack, in dem sich das Gerät befindet.</small>
</div>
<div class="form-group">
<label for="rack_position_he">Position im Rack (HE) <span class="required">*</span></label>
<input type="number" id="rack_position_he" name="rack_position_he" required min="1"
value="<?php echo htmlspecialchars($device['rack_position_he'] ?? ''); ?>"
placeholder="Höheneinheit von oben">
</div>
<div class="form-group">
<label for="rack_height_he">Höhe (HE) <span class="required">*</span></label>
<input type="number" id="rack_height_he" name="rack_height_he" required min="1"
value="<?php echo htmlspecialchars($device['rack_height_he'] ?? '1'); ?>"
placeholder="Anzahl Höheneinheiten">
</div>
</fieldset>
<fieldset>
<legend>Ports</legend>
<?php if ($isEdit): ?>
<?php if (!empty($devicePorts)): ?>
<p><small>Portstatus und VLAN-Zuordnung koennen hier direkt gepflegt werden (VLANs kommagetrennt, z. B. 10,20,30).</small></p>
<table class="device-port-table">
<thead>
<tr>
<th>Name</th>
<th>Port-Typ</th>
<th>Status</th>
<th>Modus</th>
<th>VLANs</th>
</tr>
</thead>
<tbody>
<?php foreach ($devicePorts as $port): ?>
<?php
$vlanValue = '';
if (!empty($port['vlan_config'])) {
$decodedVlans = json_decode((string)$port['vlan_config'], true);
if (is_array($decodedVlans)) {
$vlanValue = implode(', ', array_map('strval', $decodedVlans));
} else {
$vlanValue = (string)$port['vlan_config'];
}
}
$statusValue = (string)($port['status'] ?? 'active');
if (!in_array($statusValue, ['active', 'disabled'], true)) {
$statusValue = 'active';
}
?>
<tr>
<td>
<input type="text" name="device_ports[<?php echo (int)$port['id']; ?>][name]" value="<?php echo htmlspecialchars((string)$port['name']); ?>">
</td>
<td><?php echo htmlspecialchars((string)($port['port_type_name'] ?? '-')); ?></td>
<td>
<select name="device_ports[<?php echo (int)$port['id']; ?>][status]">
<option value="active" <?php echo $statusValue === 'active' ? 'selected' : ''; ?>>aktiv</option>
<option value="disabled" <?php echo $statusValue === 'disabled' ? 'selected' : ''; ?>>inaktiv</option>
</select>
</td>
<td>
<input type="text" name="device_ports[<?php echo (int)$port['id']; ?>][mode]" value="<?php echo htmlspecialchars((string)($port['mode'] ?? '')); ?>" placeholder="z. B. access/trunk">
</td>
<td>
<input type="text" name="device_ports[<?php echo (int)$port['id']; ?>][vlan_config]" value="<?php echo htmlspecialchars($vlanValue); ?>" placeholder="10,20,30">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<p><small>Zu diesem Geraet sind aktuell keine Ports vorhanden.</small></p>
<?php endif; ?>
<?php else: ?>
<p><small>Ports werden nach dem ersten Speichern automatisch aus dem Geraetetyp erzeugt und koennen dann hier gepflegt werden.</small></p>
<?php endif; ?>
</fieldset>
<!-- =========================
Aktionen
========================= -->
<fieldset class="form-actions">
<button type="submit" class="button button-primary">Speichern</button>
<a href="?module=devices&action=list" class="button">Abbrechen</a>
<?php if ($isEdit): ?>
<a href="?module=devices&action=delete&id=<?php echo (int)$deviceId; ?>" class="button button-danger" onclick="return confirmDelete(this, <?php echo (int)$deviceId; ?>, <?php echo (int)$dependencyCounts['connection_count']; ?>, <?php echo (int)$dependencyCounts['port_count']; ?>, <?php echo (int)$dependencyCounts['module_count']; ?>)">Löschen</a>
<?php endif; ?>
</fieldset>
</form>
</div>
<style>
.device-edit {
max-width: 800px;
margin: 20px auto;
padding: 20px;
}
.edit-form {
background: white;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
.edit-form fieldset {
margin: 20px 0;
padding: 15px;
border: 1px solid #e0e0e0;
border-radius: 4px;
}
.edit-form legend {
padding: 0 10px;
font-weight: bold;
font-size: 1.1em;
}
.form-group {
margin: 15px 0;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input[type="text"],
.form-group input[type="number"],
.form-group select,
.form-group textarea {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: inherit;
}
.form-group textarea {
resize: vertical;
}
.form-group small {
display: block;
margin-top: 5px;
color: #666;
}
.required {
color: red;
}
.form-actions {
display: flex;
gap: 10px;
margin-top: 30px;
}
.button {
padding: 10px 15px;
background: #007bff;
color: white;
text-decoration: none;
border-radius: 4px;
border: none;
cursor: pointer;
font-size: 0.95em;
}
.button-primary {
background: #28a745;
}
.button-danger {
background: #dc3545;
}
.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;
}
</style>
<script>
function confirmDelete(link, id, connectionCount, portCount, moduleCount) {
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.href = '?module=devices&action=list';
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>