This commit is contained in:
2026-02-13 11:23:28 +01:00
parent 8ee3252c51
commit 3507024bc3
3 changed files with 214 additions and 27 deletions

View File

@@ -71,6 +71,3 @@ if (!in_array($action, ['save', 'delete'], true)) {
require_once __DIR__ . '/templates/footer.php';
}
//TODO patchpanel bearbeiten, muss umgebaut werden: beim erstellen, wird ein panel name eingegeben, in welcher location/gebäude/stockwerk es sich befindet und vieviele ports es hat. beim editieren wird dann die stockwerk svg geladen und man kann per drag and drop das panel auf der karte an den ort ziehen wo es hingehört und speichern. Netzwerkbuchsen genauso, eigentlich sind buchsen ja nur kleine panel
//TODO buchse <-> buchse und panel <-> panal und panel <-> buche müssen verbindbar sein, im grunde sind buchsen auch nur kleine panel

View File

@@ -5,20 +5,52 @@
* Formular zum Anlegen/Bearbeiten von Patchpanels und Wandbuchsen
*/
//TODO die auswahl der stockwerke in gebäude gruppieren und gebäude in locations gruppieren
$type = $_GET['type'] ?? 'patchpanel';
$id = (int)($_GET['id'] ?? 0);
$floors = $sql->get("SELECT id, name FROM floors ORDER BY name", "", []);
$rooms = $sql->get(
"SELECT r.id, r.name, f.name AS floor_name
FROM rooms r
LEFT JOIN floors f ON f.id = r.floor_id
ORDER BY f.name, r.name",
$locations = $sql->get("SELECT id, name FROM locations ORDER BY name", "", []);
$buildings = $sql->get("SELECT id, name, location_id FROM buildings ORDER BY name", "", []);
$floors = $sql->get(
"SELECT f.*, b.name AS building_name, b.location_id, l.name AS location_name
FROM floors f
LEFT JOIN buildings b ON b.id = f.building_id
LEFT JOIN locations l ON l.id = b.location_id
ORDER BY l.name, b.name, f.level, f.name",
"",
[]
);
$rooms = $sql->get(
"SELECT r.id, r.name, r.floor_id, f.name AS floor_name, f.svg_path, b.id AS building_id, l.id AS location_id
FROM rooms r
LEFT JOIN floors f ON f.id = r.floor_id
LEFT JOIN buildings b ON b.id = f.building_id
LEFT JOIN locations l ON l.id = b.location_id
ORDER BY l.name, b.name, f.level, f.name, r.name",
"",
[]
);
$locationMap = [];
foreach ($locations as $location) {
$locationMap[$location['id']] = $location['name'];
}
foreach ($floors as &$floor) {
$paths = trim((string)($floor['svg_path'] ?? ''));
$floor['svg_url'] = $paths !== '' ? '/' . ltrim($paths, '/\\') : '';
}
unset($floor);
foreach ($rooms as &$room) {
$roomPath = trim((string)($room['svg_path'] ?? ''));
$room['floor_svg_url'] = $roomPath !== '' ? '/' . ltrim($roomPath, '/\\') : '';
}
unset($room);
$floorIndex = [];
foreach ($floors as $floor) {
$floorIndex[$floor['id']] = $floor;
}
$panel = null;
$outlet = null;
@@ -49,6 +81,17 @@ if ($type === 'outlet' && $id > 0) {
$panel = $panel ?? [];
$outlet = $outlet ?? [];
$selectedLocationId = 0;
$selectedBuildingId = 0;
$selectedFloorId = 0;
if ($type === 'patchpanel') {
$selectedFloorId = (int)($panel['floor_id'] ?? 0);
if ($selectedFloorId && isset($floorIndex[$selectedFloorId])) {
$selectedBuildingId = (int)($floorIndex[$selectedFloorId]['building_id'] ?? 0);
$selectedLocationId = (int)($floorIndex[$selectedFloorId]['location_id'] ?? 0);
}
}
$defaultPanelSize = ['width' => 140, 'height' => 40];
$defaultOutletSize = 32;
@@ -74,16 +117,47 @@ $markerHeight = $type === 'patchpanel' ? $panel['height'] : $defaultOutletSize;
<input type="text" name="name" value="<?php echo htmlspecialchars($panel['name'] ?? ''); ?>" required>
</div>
<div class="form-group">
<label>Stockwerk</label>
<select name="floor_id" required>
<option value="">- wählen -</option>
<?php foreach ($floors as $floor): ?>
<option value="<?php echo $floor['id']; ?>" <?php echo ($panel['floor_id'] ?? 0) === $floor['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($floor['name']); ?>
</option>
<?php endforeach; ?>
</select>
<div class="form-grid">
<div class="form-group">
<label>Location</label>
<select id="panel-location-select">
<option value="">- Location wählen -</option>
<?php foreach ($locations as $location): ?>
<option value="<?php echo $location['id']; ?>" <?php echo $selectedLocationId === $location['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($location['name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label>Gebäude</label>
<select id="panel-building-select">
<option value="">- Gebäude wählen -</option>
<?php foreach ($buildings as $building): ?>
<?php $buildingLocation = $locationMap[$building['location_id']] ?? ''; ?>
<option value="<?php echo $building['id']; ?>"
data-location-id="<?php echo $building['location_id'] ?? 0; ?>"
<?php echo $selectedBuildingId === $building['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($building['name'] . ($buildingLocation ? ' · ' . $buildingLocation : '')); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label>Stockwerk</label>
<select id="panel-floor-select" name="floor_id" required>
<option value="">- Stockwerk wählen -</option>
<?php foreach ($floors as $floor): ?>
<option value="<?php echo $floor['id']; ?>"
data-building-id="<?php echo $floor['building_id'] ?? 0; ?>"
data-location-id="<?php echo $floor['location_id'] ?? 0; ?>"
data-svg-url="<?php echo htmlspecialchars($floor['svg_url']); ?>"
<?php echo $selectedFloorId === $floor['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($floor['name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="form-grid">
@@ -114,6 +188,7 @@ $markerHeight = $type === 'patchpanel' ? $panel['height'] : $defaultOutletSize;
data-marker-type="patchpanel"
data-x-field="pos_x"
data-y-field="pos_y">
<img id="floor-plan-svg" class="floor-plan-svg" alt="Stockwerksplan">
<div id="floor-plan-marker" class="floor-plan-marker panel-marker"
style="--marker-width: <?php echo $markerWidth; ?>px; --marker-height: <?php echo $markerHeight; ?>px;"></div>
</div>
@@ -142,10 +217,13 @@ $markerHeight = $type === 'patchpanel' ? $panel['height'] : $defaultOutletSize;
<div class="form-group">
<label>Raum</label>
<select name="room_id" required>
<select name="room_id" id="outlet-room-select" required>
<option value="">- Raum wählen -</option>
<?php foreach ($rooms as $room): ?>
<option value="<?php echo $room['id']; ?>" <?php echo ($outlet['room_id'] ?? 0) === $room['id'] ? 'selected' : ''; ?>>
<option value="<?php echo $room['id']; ?>"
data-floor-id="<?php echo $room['floor_id'] ?? 0; ?>"
data-floor-svg-url="<?php echo htmlspecialchars($room['floor_svg_url']); ?>"
<?php echo ($outlet['room_id'] ?? 0) === $room['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($room['floor_name'] . ' / ' . $room['name']); ?>
</option>
<?php endforeach; ?>
@@ -171,6 +249,7 @@ $markerHeight = $type === 'patchpanel' ? $panel['height'] : $defaultOutletSize;
data-marker-type="outlet"
data-x-field="x"
data-y-field="y">
<img id="floor-plan-svg" class="floor-plan-svg" alt="Stockwerksplan">
<div id="floor-plan-marker" class="floor-plan-marker outlet-marker"
style="--marker-width: <?php echo $markerWidth; ?>px; --marker-height: <?php echo $markerHeight; ?>px;"></div>
</div>
@@ -245,6 +324,17 @@ $markerHeight = $type === 'patchpanel' ? $panel['height'] : $defaultOutletSize;
cursor: crosshair;
overflow: hidden;
}
.floor-plan-svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: contain;
pointer-events: none;
z-index: 0;
opacity: 0.75;
border-radius: 6px;
}
.floor-plan-marker {
position: absolute;
top: 0;
@@ -253,6 +343,7 @@ $markerHeight = $type === 'patchpanel' ? $panel['height'] : $defaultOutletSize;
height: var(--marker-height, 32px);
transition: left 0.1s ease, top 0.1s ease;
touch-action: none;
z-index: 2;
}
.floor-plan-marker.panel-marker {
background: rgba(13, 110, 253, 0.25);
@@ -379,5 +470,108 @@ document.addEventListener('DOMContentLoaded', () => {
window.addEventListener('resize', () => {
updateFromInputs();
});
const panelLocationSelect = document.getElementById('panel-location-select');
const panelBuildingSelect = document.getElementById('panel-building-select');
const panelFloorSelect = document.getElementById('panel-floor-select');
const outletRoomSelect = document.getElementById('outlet-room-select');
const floorPlanSvg = document.getElementById('floor-plan-svg');
const buildingOptions = panelBuildingSelect ? Array.from(panelBuildingSelect.options).filter((option) => option.value !== '') : [];
const floorOptions = panelFloorSelect ? Array.from(panelFloorSelect.options).filter((option) => option.value !== '') : [];
const updateFloorPlanImage = () => {
if (!floorPlanSvg) {
return;
}
const floorOption = panelFloorSelect?.selectedOptions?.[0];
const roomOption = outletRoomSelect?.selectedOptions?.[0];
const svgUrl = floorOption?.dataset?.svgUrl || roomOption?.dataset?.floorSvgUrl || '';
if (svgUrl) {
floorPlanSvg.src = svgUrl;
} else {
floorPlanSvg.removeAttribute('src');
}
};
const filterFloorOptions = () => {
if (!panelFloorSelect) {
return;
}
const buildingValue = panelBuildingSelect?.value || '';
let firstMatch = '';
floorOptions.forEach((option) => {
const matches = !buildingValue || option.dataset.buildingId === buildingValue;
option.hidden = !matches;
option.disabled = !matches;
if (matches && !firstMatch) {
firstMatch = option.value;
}
});
const selectedOption = panelFloorSelect.querySelector(`option[value="${panelFloorSelect.value}"]`);
const isSelectedHidden = selectedOption ? selectedOption.hidden : true;
if (buildingValue && (!panelFloorSelect.value || isSelectedHidden) && firstMatch) {
panelFloorSelect.value = firstMatch;
}
updateFloorPlanImage();
};
const filterBuildingOptions = () => {
if (!panelBuildingSelect) {
return;
}
const locationValue = panelLocationSelect?.value || '';
let firstMatch = '';
buildingOptions.forEach((option) => {
const matches = !locationValue || option.dataset.locationId === locationValue;
option.hidden = !matches;
option.disabled = !matches;
if (matches && !firstMatch) {
firstMatch = option.value;
}
});
const selectedOption = panelBuildingSelect.querySelector(`option[value="${panelBuildingSelect.value}"]`);
const isSelectedHidden = selectedOption ? selectedOption.hidden : true;
if (locationValue && (!panelBuildingSelect.value || isSelectedHidden) && firstMatch) {
panelBuildingSelect.value = firstMatch;
}
};
if (panelLocationSelect) {
panelLocationSelect.addEventListener('change', () => {
filterBuildingOptions();
filterFloorOptions();
});
}
if (panelBuildingSelect) {
panelBuildingSelect.addEventListener('change', () => {
filterFloorOptions();
});
}
if (panelFloorSelect) {
panelFloorSelect.addEventListener('change', () => {
updateFloorPlanImage();
});
}
if (outletRoomSelect) {
outletRoomSelect.addEventListener('change', () => {
updateFloorPlanImage();
});
}
if (panelLocationSelect) {
filterBuildingOptions();
filterFloorOptions();
} else {
updateFloorPlanImage();
}
});
</script>