Behebe Dashboard-, Loesch- und Infrastruktur-Issues

closes #20

closes #19

closes #18

closes #17
This commit is contained in:
2026-02-19 10:20:04 +01:00
parent 4214ac45d9
commit 0642a3b6ef
6 changed files with 387 additions and 24 deletions

View File

@@ -125,6 +125,58 @@ $mapOutlets = $sql->get(
"",
[]
);
$patchpanelPortOptions = $sql->get(
"SELECT
fpp.id,
fpp.name,
fp.name AS patchpanel_name,
fp.floor_id,
f.name AS floor_name,
EXISTS(
SELECT 1
FROM connections c
WHERE
((c.port_a_type = 'patchpanel' OR c.port_a_type = 'floor_patchpanel_ports') AND c.port_a_id = fpp.id)
OR
((c.port_b_type = 'patchpanel' OR c.port_b_type = 'floor_patchpanel_ports') AND c.port_b_id = fpp.id)
) AS is_occupied
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 f.name, fp.name, fpp.name",
"",
[]
);
$selectedBindPatchpanelPortId = 0;
if ($type === 'outlet' && $id > 0) {
$selectedBindPatchpanelPortId = (int)($sql->single(
"SELECT
CASE
WHEN (c.port_a_type = 'patchpanel' OR c.port_a_type = 'floor_patchpanel_ports') THEN c.port_a_id
WHEN (c.port_b_type = 'patchpanel' OR c.port_b_type = 'floor_patchpanel_ports') THEN c.port_b_id
ELSE 0
END AS patchpanel_port_id
FROM connections c
JOIN network_outlet_ports nop
ON (
((c.port_a_type = 'outlet' OR c.port_a_type = 'network_outlet_ports') AND c.port_a_id = nop.id)
OR
((c.port_b_type = 'outlet' OR c.port_b_type = 'network_outlet_ports') AND c.port_b_id = nop.id)
)
WHERE nop.outlet_id = ?
AND (
c.port_a_type = 'patchpanel' OR c.port_a_type = 'floor_patchpanel_ports'
OR
c.port_b_type = 'patchpanel' OR c.port_b_type = 'floor_patchpanel_ports'
)
ORDER BY c.id
LIMIT 1",
"i",
[$id]
)['patchpanel_port_id'] ?? 0);
}
?>
<div class="floor-infra-edit">
@@ -196,6 +248,11 @@ $mapOutlets = $sql->get(
<div id="panel-floor-plan-group" class="form-group" <?php echo $showPanelPlacementFields ? '' : 'hidden'; ?>>
<label>Stockwerkskarte</label>
<div class="floor-plan-block">
<div class="floor-plan-toolbar">
<button type="button" class="button button-small" data-floor-plan-zoom="in">+</button>
<button type="button" class="button button-small" data-floor-plan-zoom="out">-</button>
<button type="button" class="button button-small" data-floor-plan-zoom="reset">Reset</button>
</div>
<div id="floor-plan-canvas" class="floor-plan-canvas"
data-marker-width="<?php echo $markerWidth; ?>"
data-marker-height="<?php echo $markerHeight; ?>"
@@ -208,7 +265,7 @@ $mapOutlets = $sql->get(
<img id="floor-plan-svg" class="floor-plan-svg" alt="Stockwerksplan">
<svg id="floor-plan-overlay" class="floor-plan-overlay" viewBox="0 0 1 1" preserveAspectRatio="xMidYMid meet" aria-hidden="true"></svg>
</div>
<p class="floor-plan-hint">Nur das aktuell bearbeitete Patchpanel ist verschiebbar. Andere Objekte werden als Referenz halbtransparent angezeigt. Neue Objekte starten bei Position 30 x 30.</p>
<p class="floor-plan-hint">Nur das aktuell bearbeitete Patchpanel ist verschiebbar. Andere Objekte werden als Referenz halbtransparent angezeigt. Neue Objekte starten bei Position 30 x 30. Zoom per Mausrad, verschieben mit Shift + Drag.</p>
<p class="floor-plan-position">Koordinate: <span id="floor-plan-position"></span></p>
</div>
</div>
@@ -255,12 +312,49 @@ $mapOutlets = $sql->get(
</select>
</div>
<div class="form-group">
<label for="outlet-bind-patchpanel-port-id">Direkt mit Patchpanel-Port verbinden</label>
<select name="bind_patchpanel_port_id" id="outlet-bind-patchpanel-port-id">
<option value="">- Kein direkter Link -</option>
<?php foreach ($patchpanelPortOptions as $portOption): ?>
<?php
$portId = (int)($portOption['id'] ?? 0);
$isSelected = $selectedBindPatchpanelPortId === $portId;
$isOccupied = ((int)($portOption['is_occupied'] ?? 0) === 1);
$isDisabled = $isOccupied && !$isSelected;
$labelParts = array_filter([
(string)($portOption['floor_name'] ?? ''),
(string)($portOption['patchpanel_name'] ?? ''),
(string)($portOption['name'] ?? ''),
]);
$label = implode(' / ', $labelParts);
if ($isOccupied && !$isSelected) {
$label .= ' (belegt)';
}
?>
<option
value="<?php echo $portId; ?>"
data-floor-id="<?php echo (int)($portOption['floor_id'] ?? 0); ?>"
<?php echo $isSelected ? 'selected' : ''; ?>
<?php echo $isDisabled ? 'disabled' : ''; ?>>
<?php echo htmlspecialchars($label); ?>
</option>
<?php endforeach; ?>
</select>
<small>Nur Ports vom gewaehlten Stockwerk sind auswaehlbar. Beim Speichern wird die Verbindung automatisch erstellt.</small>
</div>
<input type="hidden" name="x" value="<?php echo (int)($outlet['x'] ?? 30); ?>">
<input type="hidden" name="y" value="<?php echo (int)($outlet['y'] ?? 30); ?>">
<div class="form-group">
<label>Stockwerkskarte</label>
<div class="floor-plan-block">
<div class="floor-plan-toolbar">
<button type="button" class="button button-small" data-floor-plan-zoom="in">+</button>
<button type="button" class="button button-small" data-floor-plan-zoom="out">-</button>
<button type="button" class="button button-small" data-floor-plan-zoom="reset">Reset</button>
</div>
<div id="floor-plan-canvas" class="floor-plan-canvas"
data-marker-width="<?php echo $markerWidth; ?>"
data-marker-height="<?php echo $markerHeight; ?>"
@@ -273,7 +367,7 @@ $mapOutlets = $sql->get(
<img id="floor-plan-svg" class="floor-plan-svg" alt="Stockwerksplan">
<svg id="floor-plan-overlay" class="floor-plan-overlay" viewBox="0 0 1 1" preserveAspectRatio="xMidYMid meet" aria-hidden="true"></svg>
</div>
<p class="floor-plan-hint">Nur die aktuell bearbeitete Wandbuchse ist verschiebbar. Blau = Patchpanel, Gruen = Dosen-Referenz, Orange = gewaehlter Raum. Netzwerkdosen sind immer 10 x 10.</p>
<p class="floor-plan-hint">Nur die aktuell bearbeitete Wandbuchse ist verschiebbar. Blau = Patchpanel, Gruen = Dosen-Referenz, Orange = gewaehlter Raum. Netzwerkdosen sind immer 10 x 10. Zoom per Mausrad, verschieben mit Shift + Drag.</p>
<p class="floor-plan-position">Koordinate: <span id="floor-plan-position"></span></p>
</div>
</div>

View File

@@ -80,6 +80,7 @@ if ($type === 'patchpanel') {
$x = (int)($_POST['x'] ?? 0);
$y = (int)($_POST['y'] ?? 0);
$comment = trim($_POST['comment'] ?? '');
$bindPatchpanelPortId = (int)($_POST['bind_patchpanel_port_id'] ?? 0);
$outletId = $id;
$errors = [];
@@ -126,6 +127,132 @@ if ($type === 'patchpanel') {
[$outletId]
);
}
if ($bindPatchpanelPortId > 0) {
$roomFloorId = (int)($sql->single(
"SELECT floor_id FROM rooms WHERE id = ?",
"i",
[$roomId]
)['floor_id'] ?? 0);
$patchpanelPort = $sql->single(
"SELECT
fpp.id,
fp.floor_id
FROM floor_patchpanel_ports fpp
JOIN floor_patchpanels fp ON fp.id = fpp.patchpanel_id
WHERE fpp.id = ?",
"i",
[$bindPatchpanelPortId]
);
if (!$patchpanelPort) {
$_SESSION['error'] = 'Gewaehlter Patchpanel-Port existiert nicht';
$_SESSION['validation_errors'] = ['Gewaehlter Patchpanel-Port existiert nicht'];
header('Location: ?module=floor_infrastructure&action=edit&type=outlet&id=' . $outletId);
exit;
}
if ($roomFloorId <= 0 || (int)$patchpanelPort['floor_id'] !== $roomFloorId) {
$_SESSION['error'] = 'Patchpanel-Port und Raum muessen auf demselben Stockwerk liegen';
$_SESSION['validation_errors'] = ['Patchpanel-Port und Raum muessen auf demselben Stockwerk liegen'];
header('Location: ?module=floor_infrastructure&action=edit&type=outlet&id=' . $outletId);
exit;
}
$outletPortId = (int)($sql->single(
"SELECT id
FROM network_outlet_ports
WHERE outlet_id = ?
ORDER BY id
LIMIT 1",
"i",
[$outletId]
)['id'] ?? 0);
if ($outletPortId <= 0) {
$_SESSION['error'] = 'Wandbuchsen-Port konnte nicht ermittelt werden';
$_SESSION['validation_errors'] = ['Wandbuchsen-Port konnte nicht ermittelt werden'];
header('Location: ?module=floor_infrastructure&action=edit&type=outlet&id=' . $outletId);
exit;
}
$existingPatchpanelUsage = $sql->single(
"SELECT
id,
port_a_type,
port_a_id,
port_b_type,
port_b_id
FROM connections
WHERE
((port_a_type = 'patchpanel' OR port_a_type = 'floor_patchpanel_ports') AND port_a_id = ?)
OR
((port_b_type = 'patchpanel' OR port_b_type = 'floor_patchpanel_ports') AND port_b_id = ?)
LIMIT 1",
"ii",
[$bindPatchpanelPortId, $bindPatchpanelPortId]
);
if ($existingPatchpanelUsage) {
$sameOutletConnection = (
(
(($existingPatchpanelUsage['port_a_type'] ?? '') === 'outlet' || ($existingPatchpanelUsage['port_a_type'] ?? '') === 'network_outlet_ports')
&& (int)($existingPatchpanelUsage['port_a_id'] ?? 0) === $outletPortId
)
||
(
(($existingPatchpanelUsage['port_b_type'] ?? '') === 'outlet' || ($existingPatchpanelUsage['port_b_type'] ?? '') === 'network_outlet_ports')
&& (int)($existingPatchpanelUsage['port_b_id'] ?? 0) === $outletPortId
)
);
if (!$sameOutletConnection) {
$_SESSION['error'] = 'Gewaehlter Patchpanel-Port ist bereits verbunden';
$_SESSION['validation_errors'] = ['Gewaehlter Patchpanel-Port ist bereits verbunden'];
header('Location: ?module=floor_infrastructure&action=edit&type=outlet&id=' . $outletId);
exit;
}
}
$sql->set(
"DELETE FROM connections
WHERE
((port_a_type = 'outlet' OR port_a_type = 'network_outlet_ports') AND port_a_id = ? AND (port_b_type = 'patchpanel' OR port_b_type = 'floor_patchpanel_ports'))
OR
((port_b_type = 'outlet' OR port_b_type = 'network_outlet_ports') AND port_b_id = ? AND (port_a_type = 'patchpanel' OR port_a_type = 'floor_patchpanel_ports'))",
"ii",
[$outletPortId, $outletPortId]
);
$connectionTypeId = (int)($sql->single(
"SELECT id FROM connection_types ORDER BY id LIMIT 1",
"",
[]
)['id'] ?? 0);
if ($connectionTypeId <= 0) {
$connectionTypeId = (int)$sql->set(
"INSERT INTO connection_types (name, medium, duplex, line_style, comment) VALUES (?, ?, ?, ?, ?)",
"sssss",
['Default', 'copper', 'custom', 'solid', 'Auto-created by floor_infrastructure/save'],
true
);
}
if ($connectionTypeId <= 0) {
$_SESSION['error'] = 'Kein Verbindungstyp fuer automatische Bindung verfuegbar';
$_SESSION['validation_errors'] = ['Kein Verbindungstyp fuer automatische Bindung verfuegbar'];
header('Location: ?module=floor_infrastructure&action=edit&type=outlet&id=' . $outletId);
exit;
}
$sql->set(
"INSERT INTO connections (connection_type_id, port_a_type, port_a_id, port_b_type, port_b_id, vlan_config, comment)
VALUES (?, 'outlet', ?, 'patchpanel', ?, NULL, ?)",
"iiis",
[$connectionTypeId, $outletPortId, $bindPatchpanelPortId, 'Auto-Link bei Wandbuchsen-Erstellung']
);
}
}
$_SESSION['success'] = $id > 0 ? 'Wandbuchse gespeichert' : 'Wandbuchse erstellt';
} else {