div TODOs
This commit is contained in:
35
app/modules/buildings/delete.php
Normal file
35
app/modules/buildings/delete.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* app/modules/buildings/delete.php
|
||||
*/
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Methode nicht erlaubt']);
|
||||
exit;
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$id = (int)($_POST['id'] ?? $_GET['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'ID fehlt']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$exists = $sql->single("SELECT id FROM buildings WHERE id = ?", "i", [$id]);
|
||||
if (!$exists) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Gebaeude nicht gefunden']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$rows = $sql->set("DELETE FROM buildings WHERE id = ?", "i", [$id]);
|
||||
if ($rows === false) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Loeschen fehlgeschlagen']);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['status' => 'ok', 'success' => true, 'rows' => $rows]);
|
||||
@@ -47,7 +47,7 @@ $selectedLocationId = $building['location_id'] ?? $prefillLocationId;
|
||||
<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($building['name'] ?? ''); ?>"
|
||||
value="<?php echo htmlspecialchars($building['name'] ?? '); ?>"
|
||||
placeholder="z.B. Gebäude A, Verwaltungsgebäude">
|
||||
</div>
|
||||
|
||||
@@ -57,7 +57,7 @@ $selectedLocationId = $building['location_id'] ?? $prefillLocationId;
|
||||
<option value="">- Wählen -</option>
|
||||
<?php foreach ($locations as $location): ?>
|
||||
<option value="<?php echo $location['id']; ?>"
|
||||
<?php echo ((int)$selectedLocationId === (int)$location['id']) ? 'selected' : ''; ?>>
|
||||
<?php echo ((int)$selectedLocationId === (int)$location['id']) ? 'selected' : '; ?>>
|
||||
<?php echo htmlspecialchars($location['name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
@@ -67,7 +67,7 @@ $selectedLocationId = $building['location_id'] ?? $prefillLocationId;
|
||||
<div class="form-group">
|
||||
<label for="comment">Beschreibung</label>
|
||||
<textarea id="comment" name="comment" rows="3"
|
||||
placeholder="Adresse, Besonderheiten, etc."><?php echo htmlspecialchars($building['comment'] ?? ''); ?></textarea>
|
||||
placeholder="Adresse, Besonderheiten, etc."><?php echo htmlspecialchars($building['comment'] ?? '); ?></textarea>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
@@ -172,9 +172,25 @@ $selectedLocationId = $building['location_id'] ?? $prefillLocationId;
|
||||
|
||||
<script>
|
||||
function confirmDelete(id) {
|
||||
if (confirm('Dieses Gebäude wirklich löschen? Alle Stockwerke werden gelöscht.')) {
|
||||
// TODO: AJAX-Delete implementieren
|
||||
alert('Löschen noch nicht implementiert');
|
||||
if (confirm('Dieses Gebaeude wirklich loeschen? Alle Stockwerke werden geloescht.')) {
|
||||
fetch('?module=buildings&action=delete', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
|
||||
body: 'id=' + encodeURIComponent(id)
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (data && data.status === 'ok') {
|
||||
window.location.href = '?module=buildings&action=list';
|
||||
return;
|
||||
}
|
||||
alert((data && data.error) ? data.error : 'Loeschen fehlgeschlagen');
|
||||
})
|
||||
.catch(() => {
|
||||
alert('Loeschen fehlgeschlagen');
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -8,17 +8,17 @@
|
||||
// =========================
|
||||
// Filter einlesen
|
||||
// =========================
|
||||
$search = trim($_GET['search'] ?? '');
|
||||
$search = trim($_GET['search'] ?? ');
|
||||
$locationId = (int)($_GET['location_id'] ?? 0);
|
||||
|
||||
// =========================
|
||||
// WHERE-Clause bauen
|
||||
// =========================
|
||||
$where = [];
|
||||
$types = '';
|
||||
$types = ';
|
||||
$params = [];
|
||||
|
||||
if ($search !== '') {
|
||||
if ($search !== ') {
|
||||
$where[] = "b.name LIKE ? OR b.comment LIKE ?";
|
||||
$types .= "ss";
|
||||
$params[] = "%$search%";
|
||||
@@ -70,7 +70,7 @@ $locations = $sql->get("SELECT id, name FROM locations ORDER BY name", "", []);
|
||||
<option value="">- Alle Standorte -</option>
|
||||
<?php foreach ($locations as $loc): ?>
|
||||
<option value="<?php echo $loc['id']; ?>"
|
||||
<?php echo $loc['id'] === $locationId ? 'selected' : ''; ?>>
|
||||
<?php echo $loc['id'] === $locationId ? 'selected' : '; ?>>
|
||||
<?php echo htmlspecialchars($loc['name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
@@ -112,7 +112,7 @@ $locations = $sql->get("SELECT id, name FROM locations ORDER BY name", "", []);
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<small><?php echo htmlspecialchars($building['comment'] ?? ''); ?></small>
|
||||
<small><?php echo htmlspecialchars($building['comment'] ?? '); ?></small>
|
||||
</td>
|
||||
|
||||
<td class="actions">
|
||||
@@ -241,9 +241,24 @@ $locations = $sql->get("SELECT id, name FROM locations ORDER BY name", "", []);
|
||||
|
||||
<script>
|
||||
function confirmDelete(id) {
|
||||
if (confirm('Dieses Gebäude wirklich löschen?')) {
|
||||
// TODO: AJAX-Delete implementieren
|
||||
alert('Löschen noch nicht implementiert');
|
||||
if (confirm('Dieses Gebaeude wirklich loeschen?')) {
|
||||
fetch('?module=buildings&action=delete', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
|
||||
body: 'id=' + encodeURIComponent(id)
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (data && data.status === 'ok') {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
alert((data && data.error) ? data.error : 'Loeschen fehlgeschlagen');
|
||||
})
|
||||
.catch(() => {
|
||||
alert('Loeschen fehlgeschlagen');
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ $recentDevices = $sql->get(
|
||||
<!-- Dashboard / Übersicht -->
|
||||
<div class="dashboard">
|
||||
<h1>Dashboard</h1>
|
||||
<div id="dashboard-modules" class="dashboard-modules"></div>
|
||||
<p data-dashboard-stats class="dashboard-inline-status"></p>
|
||||
<p data-dashboard-warnings class="dashboard-inline-status"></p>
|
||||
<p data-dashboard-recent class="dashboard-inline-status"></p>
|
||||
|
||||
<!-- Statistik-Karten -->
|
||||
<div class="stats-grid">
|
||||
@@ -100,6 +104,54 @@ $recentDevices = $sql->get(
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.dashboard-modules {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 12px;
|
||||
margin: 12px 0 20px;
|
||||
}
|
||||
|
||||
.dashboard-tile {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
border: 1px solid #d7d7d7;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
text-decoration: none;
|
||||
color: #222;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.dashboard-icon {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 999px;
|
||||
background: #0c4da2;
|
||||
color: #fff;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.dashboard-content h3 {
|
||||
margin: 0 0 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.dashboard-content p {
|
||||
margin: 0;
|
||||
font-size: 0.85rem;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.dashboard-inline-status {
|
||||
margin: 6px 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
border: 1px solid #ddd;
|
||||
padding: 20px;
|
||||
@@ -143,4 +195,4 @@ $recentDevices = $sql->get(
|
||||
.recent-devices th {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
39
app/modules/device_types/delete.php
Normal file
39
app/modules/device_types/delete.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* app/modules/device_types/delete.php
|
||||
*/
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'message' => 'Methode nicht erlaubt']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int)($_POST['id'] ?? $_GET['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'ID fehlt']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$exists = $sql->single("SELECT id FROM device_types WHERE id = ?", "i", [$id]);
|
||||
if (!$exists) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['success' => false, 'message' => 'Geraetetyp nicht gefunden']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$rows = $sql->set("DELETE FROM device_types WHERE id = ?", "i", [$id]);
|
||||
if ($rows === false) {
|
||||
http_response_code(409);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Geraetetyp konnte nicht geloescht werden (wird ggf. noch von Geraeten verwendet)'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Geraetetyp geloescht']);
|
||||
|
||||
@@ -15,7 +15,6 @@ $deviceTypeId = (int)($_GET['id'] ?? 0);
|
||||
$deviceType = null;
|
||||
$ports = [];
|
||||
|
||||
//TODO port hinzufügen geht nicht
|
||||
|
||||
if ($deviceTypeId > 0) {
|
||||
$deviceType = $sql->single(
|
||||
@@ -339,3 +338,4 @@ $pageTitle = $isEdit ? "Gerätetyp bearbeiten: " . htmlspecialchars($deviceType[
|
||||
</div>
|
||||
<script src="/assets/js/device-type-shape-editor.js" defer></script>
|
||||
<script src="/assets/js/device-type-edit-form.js" defer></script>
|
||||
|
||||
|
||||
@@ -1,265 +1,165 @@
|
||||
<?php
|
||||
/**
|
||||
* app/device_types/ports.php
|
||||
* app/modules/device_types/ports.php
|
||||
*
|
||||
* Verwaltung der Ports eines Gerätetyps
|
||||
* - Port anlegen / bearbeiten / löschen
|
||||
* - Port-Typ (RJ45, SFP, BNC, Custom)
|
||||
* - VLAN / Modus / Medienart
|
||||
* - Übergabe an SVG-Port-Editor
|
||||
* Verwaltung der Ports eines Geraetetyps.
|
||||
*/
|
||||
|
||||
// TODO: bootstrap laden
|
||||
// require_once __DIR__ . '/../../bootstrap.php';
|
||||
$deviceTypeId = (int)($_GET['id'] ?? ($_GET['device_type_id'] ?? 0));
|
||||
if ($deviceTypeId <= 0) {
|
||||
renderClientError(400, 'device_type_id fehlt');
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Auth erzwingen
|
||||
// requireAuth();
|
||||
$deviceType = $sql->single(
|
||||
"SELECT id, name, image_path, image_type FROM device_types WHERE id = ?",
|
||||
'i',
|
||||
[$deviceTypeId]
|
||||
);
|
||||
|
||||
// =========================
|
||||
// Kontext bestimmen
|
||||
// =========================
|
||||
if (!$deviceType) {
|
||||
renderClientError(404, 'Geraetetyp nicht gefunden');
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: device_type_id aus GET lesen
|
||||
// $deviceTypeId = (int)($_GET['id'] ?? 0);
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$formAction = $_POST['form_action'] ?? '';
|
||||
|
||||
// TODO: Gerätetyp laden
|
||||
// $deviceType = null;
|
||||
if ($formAction === 'add_port') {
|
||||
$name = trim((string)($_POST['name'] ?? ''));
|
||||
$portTypeId = (int)($_POST['port_type_id'] ?? 0);
|
||||
$x = (int)($_POST['x'] ?? 0);
|
||||
$y = (int)($_POST['y'] ?? 0);
|
||||
|
||||
// TODO: Ports dieses Gerätetyps laden
|
||||
// $ports = [];
|
||||
if ($name !== '') {
|
||||
if ($portTypeId > 0) {
|
||||
$sql->set(
|
||||
"INSERT INTO device_type_ports (device_type_id, name, port_type_id, x, y) VALUES (?, ?, ?, ?, ?)",
|
||||
"isiii",
|
||||
[$deviceTypeId, $name, $portTypeId, $x, $y]
|
||||
);
|
||||
} else {
|
||||
$sql->set(
|
||||
"INSERT INTO device_type_ports (device_type_id, name, port_type_id, x, y) VALUES (?, ?, NULL, ?, ?)",
|
||||
"isii",
|
||||
[$deviceTypeId, $name, $x, $y]
|
||||
);
|
||||
}
|
||||
$_SESSION['success'] = 'Port hinzugefuegt';
|
||||
} else {
|
||||
$_SESSION['error'] = 'Portname darf nicht leer sein';
|
||||
}
|
||||
|
||||
header('Location: ?module=device_types&action=ports&id=' . $deviceTypeId);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($formAction === 'delete_port') {
|
||||
$portId = (int)($_POST['port_id'] ?? 0);
|
||||
if ($portId > 0) {
|
||||
$sql->set(
|
||||
"DELETE FROM device_type_ports WHERE id = ? AND device_type_id = ?",
|
||||
"ii",
|
||||
[$portId, $deviceTypeId]
|
||||
);
|
||||
$_SESSION['success'] = 'Port geloescht';
|
||||
}
|
||||
|
||||
header('Location: ?module=device_types&action=ports&id=' . $deviceTypeId);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$ports = $sql->get(
|
||||
"SELECT dtp.id, dtp.name, dtp.port_type_id, dtp.x, dtp.y, pt.name AS port_type_name
|
||||
FROM device_type_ports dtp
|
||||
LEFT JOIN port_types pt ON pt.id = dtp.port_type_id
|
||||
WHERE dtp.device_type_id = ?
|
||||
ORDER BY dtp.id ASC",
|
||||
'i',
|
||||
[$deviceTypeId]
|
||||
);
|
||||
|
||||
$portTypes = $sql->get("SELECT id, name FROM port_types ORDER BY name", '', []);
|
||||
$svgPath = trim((string)($deviceType['image_path'] ?? ''));
|
||||
$svgUrl = $svgPath !== '' ? '/' . ltrim($svgPath, '/\\') : '';
|
||||
?>
|
||||
|
||||
<h2>Ports – Gerätetyp</h2>
|
||||
<div class="device-type-ports">
|
||||
<h2>Ports: <?php echo htmlspecialchars((string)$deviceType['name']); ?></h2>
|
||||
|
||||
<!-- =========================
|
||||
Zurück / Kontext
|
||||
========================= -->
|
||||
<div class="breadcrumb">
|
||||
<a href="?module=device_types&action=list">Geraetetypen</a>
|
||||
->
|
||||
<a href="?module=device_types&action=edit&id=<?php echo (int)$deviceType['id']; ?>"><?php echo htmlspecialchars((string)$deviceType['name']); ?></a>
|
||||
-> Ports
|
||||
</div>
|
||||
|
||||
<div class="breadcrumb">
|
||||
<a href="/?page=device_types/list">Gerätetypen</a>
|
||||
→
|
||||
<a href="/?page=device_types/edit&id=<?= $deviceTypeId ?>">
|
||||
<!-- TODO: Gerätetyp-Name -->
|
||||
Gerätetyp
|
||||
</a>
|
||||
→
|
||||
Ports
|
||||
<div class="toolbar">
|
||||
<a class="button" href="?module=device_types&action=edit&id=<?php echo (int)$deviceType['id']; ?>">Zurueck zum Geraetetyp</a>
|
||||
</div>
|
||||
|
||||
<form method="post" class="port-form">
|
||||
<input type="hidden" name="form_action" value="add_port">
|
||||
<div>
|
||||
<label for="name">Portname</label>
|
||||
<input id="name" name="name" required placeholder="z. B. Gi1/0/1">
|
||||
</div>
|
||||
<div>
|
||||
<label for="port_type_id">Port-Typ</label>
|
||||
<select id="port_type_id" name="port_type_id">
|
||||
<option value="0">- keiner -</option>
|
||||
<?php foreach ($portTypes as $portType): ?>
|
||||
<option value="<?php echo (int)$portType['id']; ?>"><?php echo htmlspecialchars((string)$portType['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="x">X</label>
|
||||
<input id="x" name="x" type="number" value="0">
|
||||
</div>
|
||||
<div>
|
||||
<label for="y">Y</label>
|
||||
<input id="y" name="y" type="number" value="0">
|
||||
</div>
|
||||
<button type="submit" class="button button-primary">Port hinzufuegen</button>
|
||||
</form>
|
||||
|
||||
<table class="port-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Name</th>
|
||||
<th>Typ</th>
|
||||
<th>X</th>
|
||||
<th>Y</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($ports as $port): ?>
|
||||
<tr>
|
||||
<td><?php echo (int)$port['id']; ?></td>
|
||||
<td><?php echo htmlspecialchars((string)$port['name']); ?></td>
|
||||
<td><?php echo htmlspecialchars((string)($port['port_type_name'] ?? '-')); ?></td>
|
||||
<td><?php echo (int)$port['x']; ?></td>
|
||||
<td><?php echo (int)$port['y']; ?></td>
|
||||
<td>
|
||||
<form method="post" onsubmit="return confirm('Port wirklich loeschen?');" style="display:inline;">
|
||||
<input type="hidden" name="form_action" value="delete_port">
|
||||
<input type="hidden" name="port_id" value="<?php echo (int)$port['id']; ?>">
|
||||
<button type="submit" class="button button-small button-danger">Loeschen</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php if ($svgUrl !== '' && ($deviceType['image_type'] ?? '') === 'svg'): ?>
|
||||
<section class="svg-port-editor-section">
|
||||
<h3>SVG Vorschau</h3>
|
||||
<img src="<?php echo htmlspecialchars($svgUrl); ?>" alt="Geraetetyp SVG" style="max-width:100%; height:auto; border:1px solid #ddd;">
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- =========================
|
||||
Toolbar
|
||||
========================= -->
|
||||
|
||||
<div class="toolbar">
|
||||
<button id="add-port">
|
||||
+ Port hinzufügen
|
||||
</button>
|
||||
|
||||
<!-- TODO: Port-Typen verwalten -->
|
||||
<!-- TODO: Import / Export -->
|
||||
</div>
|
||||
|
||||
<form id="port-form" class="port-form" aria-hidden="true">
|
||||
<div>
|
||||
<label for="port-name">Portname</label>
|
||||
<input id="port-name" name="name" required placeholder="z. B. Gi1/0/1">
|
||||
</div>
|
||||
<div>
|
||||
<label for="port-type">Port-Typ</label>
|
||||
<input id="port-type" name="type" required placeholder="RJ45, SFP …">
|
||||
</div>
|
||||
<div>
|
||||
<label for="port-medium">Medium</label>
|
||||
<input id="port-medium" name="medium" placeholder="Kupfer, LWL …">
|
||||
</div>
|
||||
<div>
|
||||
<label for="port-mode">Modus</label>
|
||||
<input id="port-mode" name="mode" placeholder="Access, Trunk …">
|
||||
</div>
|
||||
<div>
|
||||
<label for="port-vlan">VLAN</label>
|
||||
<input id="port-vlan" name="vlan" placeholder="10, 20-30 …">
|
||||
</div>
|
||||
<button type="submit" class="button button-primary">Port hinzufügen</button>
|
||||
<button type="button" class="button" id="cancel-port">Abbrechen</button>
|
||||
</form>
|
||||
|
||||
<!-- =========================
|
||||
Port-Liste
|
||||
========================= -->
|
||||
|
||||
<table class="port-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Name</th>
|
||||
<th>Typ</th>
|
||||
<th>Medium</th>
|
||||
<th>Modus</th>
|
||||
<th>VLAN</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="port-list-body">
|
||||
|
||||
<?php /* foreach ($ports as $port): */ ?>
|
||||
<tr>
|
||||
<td>
|
||||
<!-- TODO: Port-Nummer -->
|
||||
1
|
||||
</td>
|
||||
<td>
|
||||
<!-- TODO: Port-Name -->
|
||||
Port 1
|
||||
</td>
|
||||
<td>
|
||||
<!-- TODO: Port-Typ (RJ45, SFP, ...) -->
|
||||
</td>
|
||||
<td>
|
||||
<!-- TODO: Medium (Kupfer, LWL, BNC, Custom) -->
|
||||
</td>
|
||||
<td>
|
||||
<!-- TODO: Modus (Access, Trunk, Custom) -->
|
||||
</td>
|
||||
<td>
|
||||
<!-- TODO: VLANs -->
|
||||
</td>
|
||||
<td>
|
||||
<button>
|
||||
Bearbeiten
|
||||
</button>
|
||||
|
||||
<button>
|
||||
Löschen
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php /* endforeach; */ ?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- =========================
|
||||
SVG-Port-Positionierung
|
||||
========================= -->
|
||||
|
||||
<section class="svg-port-editor-section">
|
||||
<h3>Port-Positionen</h3>
|
||||
|
||||
<p class="hint">
|
||||
Ports per Drag & Drop auf dem Gerät platzieren.
|
||||
</p>
|
||||
|
||||
<div class="svg-editor-container">
|
||||
<svg
|
||||
id="device-type-svg"
|
||||
viewBox="0 0 800 400"
|
||||
width="100%"
|
||||
height="400"
|
||||
>
|
||||
<!-- TODO: SVG des Gerätetyps laden -->
|
||||
</svg>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- =========================
|
||||
JS-Konfiguration
|
||||
========================= -->
|
||||
|
||||
<style>
|
||||
.port-form {
|
||||
display: none;
|
||||
margin: 20px 0;
|
||||
gap: 10px;
|
||||
padding: 15px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
background: #f9f9f9;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
grid-auto-rows: minmax(50px, auto);
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.port-form.visible {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.port-form div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.port-form label {
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.port-form input {
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #bbb;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.port-form button {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.port-form .button-primary {
|
||||
justify-self: flex-start;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const addPortButton = document.getElementById('add-port');
|
||||
const portForm = document.getElementById('port-form');
|
||||
const portListBody = document.getElementById('port-list-body');
|
||||
const cancelPortButton = document.getElementById('cancel-port');
|
||||
let portCounter = portListBody.querySelectorAll('tr').length + 1;
|
||||
|
||||
function showPortForm(show = true) {
|
||||
portForm.classList.toggle('visible', show);
|
||||
portForm.setAttribute('aria-hidden', show ? 'false' : 'true');
|
||||
if (show) {
|
||||
portForm.querySelector('input').focus();
|
||||
}
|
||||
}
|
||||
|
||||
addPortButton.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
showPortForm(portForm.getAttribute('aria-hidden') === 'true');
|
||||
});
|
||||
|
||||
cancelPortButton.addEventListener('click', () => {
|
||||
showPortForm(false);
|
||||
portForm.reset();
|
||||
});
|
||||
|
||||
portForm.addEventListener('submit', (event) => {
|
||||
event.preventDefault();
|
||||
const data = new FormData(portForm);
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>${portCounter++}</td>
|
||||
<td>${data.get('name') || '-'}</td>
|
||||
<td>${data.get('type') || '-'}</td>
|
||||
<td>${data.get('medium') || '-'}</td>
|
||||
<td>${data.get('mode') || '-'}</td>
|
||||
<td>${data.get('vlan') || '-'}</td>
|
||||
<td>
|
||||
<button type="button">Bearbeiten</button>
|
||||
<button type="button">Löschen</button>
|
||||
</td>
|
||||
`;
|
||||
portListBody.appendChild(row);
|
||||
showPortForm(false);
|
||||
portForm.reset();
|
||||
});
|
||||
|
||||
/**
|
||||
* TODO: Replace this mock logic with real AJAX once ports are
|
||||
* persisted on the backend.
|
||||
*/
|
||||
</script>
|
||||
|
||||
31
app/modules/floor_infrastructure/delete.php
Normal file
31
app/modules/floor_infrastructure/delete.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* app/modules/floor_infrastructure/delete.php
|
||||
*
|
||||
* Loescht Patchpanels oder Wandbuchsen.
|
||||
*/
|
||||
|
||||
$type = strtolower(trim((string)($_GET['type'] ?? '')));
|
||||
$id = (int)($_GET['id'] ?? 0);
|
||||
|
||||
if ($id <= 0 || !in_array($type, ['patchpanel', 'outlet'], true)) {
|
||||
header('Location: ?module=floor_infrastructure&action=list');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($type === 'patchpanel') {
|
||||
$sql->set(
|
||||
"DELETE FROM floor_patchpanels WHERE id = ?",
|
||||
"i",
|
||||
[$id]
|
||||
);
|
||||
} else {
|
||||
$sql->set(
|
||||
"DELETE FROM network_outlets WHERE id = ?",
|
||||
"i",
|
||||
[$id]
|
||||
);
|
||||
}
|
||||
|
||||
header('Location: ?module=floor_infrastructure&action=list');
|
||||
exit;
|
||||
@@ -293,11 +293,8 @@ $mapOutlets = $sql->get(
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
//TODO drag an drop auf der stockwerkskarte für die patchfelder und wandbuchsen. buchsen haben eine einheitliche größe, und sind quadratisch, patchfelder sind auch für sich einheitlich, sind rechteckig und breiter als hoch
|
||||
//TODO style in css files einsortieren
|
||||
?>
|
||||
<script src="/assets/js/floor-infrastructure-edit.js" defer></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -180,6 +180,7 @@ if ($editorFloor) {
|
||||
<td><?php echo (int)$panel['port_count']; ?></td>
|
||||
<td class="actions">
|
||||
<a href="?module=floor_infrastructure&action=edit&type=patchpanel&id=<?php echo (int)$panel['id']; ?>" class="button button-small">Bearbeiten</a>
|
||||
<a href="?module=floor_infrastructure&action=delete&type=patchpanel&id=<?php echo (int)$panel['id']; ?>" class="button button-small button-danger" onclick="return confirm('Patchpanel wirklich loeschen?');">Loeschen</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
@@ -225,6 +226,7 @@ if ($editorFloor) {
|
||||
<td><?php echo htmlspecialchars((string)($outlet['comment'] ?? '')); ?></td>
|
||||
<td class="actions">
|
||||
<a href="?module=floor_infrastructure&action=edit&type=outlet&id=<?php echo (int)$outlet['id']; ?>" class="button button-small">Bearbeiten</a>
|
||||
<a href="?module=floor_infrastructure&action=delete&type=outlet&id=<?php echo (int)$outlet['id']; ?>" class="button button-small button-danger" onclick="return confirm('Wandbuchse wirklich loeschen?');">Loeschen</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
||||
36
app/modules/floors/delete.php
Normal file
36
app/modules/floors/delete.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* app/modules/floors/delete.php
|
||||
*/
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'message' => 'Methode nicht erlaubt']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int)($_POST['id'] ?? $_GET['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'ID fehlt']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$exists = $sql->single("SELECT id FROM floors WHERE id = ?", "i", [$id]);
|
||||
if (!$exists) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['success' => false, 'message' => 'Stockwerk nicht gefunden']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$rows = $sql->set("DELETE FROM floors WHERE id = ?", "i", [$id]);
|
||||
if ($rows === false) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Loeschen fehlgeschlagen']);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Stockwerk geloescht']);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
// =========================
|
||||
// Filter einlesen
|
||||
// =========================
|
||||
$search = trim($_GET['search'] ?? '');
|
||||
$search = trim($_GET['search'] ?? ');
|
||||
|
||||
// =========================
|
||||
// Floors laden
|
||||
@@ -19,7 +19,7 @@ $whereClause = "";
|
||||
$types = "";
|
||||
$params = [];
|
||||
|
||||
if ($search !== '') {
|
||||
if ($search !== ') {
|
||||
$whereClause = "WHERE f.name LIKE ? OR f.comment LIKE ?";
|
||||
$types = "ss";
|
||||
$params = ["%$search%", "%$search%"];
|
||||
@@ -105,7 +105,7 @@ $buildings = $sql->get("SELECT id, name FROM buildings ORDER BY name", "", []);
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<small><?php echo htmlspecialchars($floor['comment'] ?? ''); ?></small>
|
||||
<small><?php echo htmlspecialchars($floor['comment'] ?? '); ?></small>
|
||||
</td>
|
||||
|
||||
<td class="actions">
|
||||
@@ -233,9 +233,24 @@ $buildings = $sql->get("SELECT id, name FROM buildings ORDER BY name", "", []);
|
||||
|
||||
<script>
|
||||
function confirmDelete(id) {
|
||||
if (confirm('Dieses Stockwerk wirklich löschen? Alle Räume und Racks werden gelöscht.')) {
|
||||
// TODO: AJAX-Delete implementieren
|
||||
alert('Löschen noch nicht implementiert');
|
||||
if (confirm('Dieses Stockwerk wirklich loeschen? Alle Raeume und Racks werden geloescht.')) {
|
||||
fetch('?module=floors&action=delete', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
|
||||
body: 'id=' + encodeURIComponent(id)
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (data && data.success) {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
alert((data && data.message) ? data.message : 'Loeschen fehlgeschlagen');
|
||||
})
|
||||
.catch(() => {
|
||||
alert('Loeschen fehlgeschlagen');
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -42,14 +42,14 @@ $pageTitle = $isEdit ? "Standort bearbeiten: " . htmlspecialchars($location['nam
|
||||
<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($location['name'] ?? ''); ?>"
|
||||
value="<?php echo htmlspecialchars($location['name'] ?? '); ?>"
|
||||
placeholder="z.B. Hauptgebäude, Campus A, Außenstelle Berlin">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="comment">Beschreibung</label>
|
||||
<textarea id="comment" name="comment" rows="3"
|
||||
placeholder="Adresse, Kontaktinformationen, Besonderheiten"><?php echo htmlspecialchars($location['comment'] ?? ''); ?></textarea>
|
||||
placeholder="Adresse, Kontaktinformationen, Besonderheiten"><?php echo htmlspecialchars($location['comment'] ?? '); ?></textarea>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
@@ -153,9 +153,24 @@ $pageTitle = $isEdit ? "Standort bearbeiten: " . htmlspecialchars($location['nam
|
||||
|
||||
<script>
|
||||
function confirmDelete(id) {
|
||||
if (confirm('Diesen Standort wirklich löschen? Alle Gebäude werden gelöscht.')) {
|
||||
// TODO: AJAX-Delete implementieren
|
||||
alert('Löschen noch nicht implementiert');
|
||||
if (confirm('Diesen Standort wirklich loeschen? Alle Gebaeude werden geloescht.')) {
|
||||
fetch('?module=locations&action=delete', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
|
||||
body: 'id=' + encodeURIComponent(id)
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (data && data.success) {
|
||||
window.location.href = '?module=locations&action=list';
|
||||
return;
|
||||
}
|
||||
alert((data && data.message) ? data.message : 'Loeschen fehlgeschlagen');
|
||||
})
|
||||
.catch(() => {
|
||||
alert('Loeschen fehlgeschlagen');
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
36
app/modules/racks/delete.php
Normal file
36
app/modules/racks/delete.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* app/modules/racks/delete.php
|
||||
*/
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'message' => 'Methode nicht erlaubt']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int)($_POST['id'] ?? $_GET['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'ID fehlt']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$exists = $sql->single("SELECT id FROM racks WHERE id = ?", "i", [$id]);
|
||||
if (!$exists) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['success' => false, 'message' => 'Rack nicht gefunden']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$rows = $sql->set("DELETE FROM racks WHERE id = ?", "i", [$id]);
|
||||
if ($rows === false) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Loeschen fehlgeschlagen']);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Rack geloescht']);
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* app/modules/racks/edit.php
|
||||
*
|
||||
* Rack anlegen oder bearbeiten
|
||||
* - Name, Beschreibung
|
||||
* - Zugehöriges Stockwerk (Floor)
|
||||
* - Höhe in Höheneinheiten (HE)
|
||||
*/
|
||||
|
||||
// =========================
|
||||
// Kontext bestimmen
|
||||
// =========================
|
||||
$rackId = (int)($_GET['id'] ?? 0);
|
||||
$rack = null;
|
||||
|
||||
@@ -23,83 +15,61 @@ if ($rackId > 0) {
|
||||
}
|
||||
|
||||
$isEdit = !empty($rack);
|
||||
$pageTitle = $isEdit ? "Rack bearbeiten: " . htmlspecialchars($rack['name']) : "Neues Rack";
|
||||
|
||||
// =========================
|
||||
// Floors laden
|
||||
// =========================
|
||||
$floors = $sql->get("SELECT id, name FROM floors ORDER BY name", "", []);
|
||||
|
||||
$pageTitle = $isEdit ? 'Rack bearbeiten: ' . htmlspecialchars((string)$rack['name']) : 'Neues Rack';
|
||||
$floors = $sql->get("SELECT id, name FROM floors ORDER BY name", '', []);
|
||||
?>
|
||||
|
||||
<div class="rack-edit">
|
||||
<h1><?php echo $pageTitle; ?></h1>
|
||||
|
||||
<form method="post" action="?module=racks&action=save" class="edit-form">
|
||||
|
||||
<?php if ($isEdit): ?>
|
||||
<input type="hidden" name="id" value="<?php echo $rackId; ?>">
|
||||
<input type="hidden" name="id" value="<?php echo (int)$rackId; ?>">
|
||||
<?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($rack['name'] ?? ''); ?>"
|
||||
placeholder="z.B. Rack A1">
|
||||
<input type="text" id="name" name="name" required value="<?php echo htmlspecialchars((string)($rack['name'] ?? '')); ?>" placeholder="z.B. Rack A1">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="comment">Beschreibung</label>
|
||||
<textarea id="comment" name="comment" rows="3"
|
||||
placeholder="z.B. Standort, Besonderheiten"><?php echo htmlspecialchars($rack['comment'] ?? ''); ?></textarea>
|
||||
<textarea id="comment" name="comment" rows="3" placeholder="z.B. Standort, Besonderheiten"><?php echo htmlspecialchars((string)($rack['comment'] ?? '')); ?></textarea>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- =========================
|
||||
Standort & Höhe
|
||||
========================= -->
|
||||
<fieldset>
|
||||
<legend>Standort & Größe</legend>
|
||||
<legend>Standort und Groesse</legend>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="floor_id">Stockwerk <span class="required">*</span></label>
|
||||
<select id="floor_id" name="floor_id" required>
|
||||
<option value="">- Wählen -</option>
|
||||
<option value="">- Waehlen -</option>
|
||||
<?php foreach ($floors as $floor): ?>
|
||||
<option value="<?php echo $floor['id']; ?>"
|
||||
<?php echo ($rack['floor_id'] ?? 0) == $floor['id'] ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($floor['name']); ?>
|
||||
</option>
|
||||
<option value="<?php echo (int)$floor['id']; ?>" <?php echo ((int)($rack['floor_id'] ?? 0) === (int)$floor['id']) ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars((string)$floor['name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="height_he">Höhe in Höheneinheiten (HE) <span class="required">*</span></label>
|
||||
<input type="number" id="height_he" name="height_he" required min="1" max="100"
|
||||
value="<?php echo htmlspecialchars($rack['height_he'] ?? '42'); ?>"
|
||||
placeholder="z.B. 42">
|
||||
<small>Standard: 42 HE (ca. 2 Meter)</small>
|
||||
<label for="height_he">Hoehe in Hoeheneinheiten (HE) <span class="required">*</span></label>
|
||||
<input type="number" id="height_he" name="height_he" required min="1" max="100" value="<?php echo (int)($rack['height_he'] ?? 42); ?>">
|
||||
<small>Standard: 42 HE</small>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- =========================
|
||||
Aktionen
|
||||
========================= -->
|
||||
<fieldset class="form-actions">
|
||||
<button type="submit" class="button button-primary">Speichern</button>
|
||||
<a href="?module=racks&action=list" class="button">Abbrechen</a>
|
||||
<?php if ($isEdit): ?>
|
||||
<a href="#" class="button button-danger" onclick="confirmDelete(<?php echo $rackId; ?>)">Löschen</a>
|
||||
<a href="#" class="button button-danger" onclick="return confirmDelete(<?php echo (int)$rackId; ?>)">Loeschen</a>
|
||||
<?php endif; ?>
|
||||
</fieldset>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -197,60 +167,25 @@ $floors = $sql->get("SELECT id, name FROM floors ORDER BY name", "", []);
|
||||
|
||||
<script>
|
||||
function confirmDelete(id) {
|
||||
if (confirm('Dieses Rack wirklich löschen? Alle Geräte werden aus dem Rack entfernt.')) {
|
||||
// TODO: AJAX-Delete implementieren
|
||||
alert('Löschen noch nicht implementiert');
|
||||
if (!confirm('Dieses Rack wirklich loeschen? Alle Geraete werden aus dem Rack entfernt.')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fetch('?module=racks&action=delete', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
|
||||
body: 'id=' + encodeURIComponent(id)
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data && data.success) {
|
||||
window.location.href = '?module=racks&action=list';
|
||||
return;
|
||||
}
|
||||
alert((data && data.message) ? data.message : 'Loeschen fehlgeschlagen');
|
||||
})
|
||||
.catch(() => alert('Loeschen fehlgeschlagen'));
|
||||
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- =========================
|
||||
Rack-SVG / Gerätepositionen
|
||||
========================= -->
|
||||
|
||||
<fieldset>
|
||||
<legend>Rack-Layout</legend>
|
||||
|
||||
<div class="svg-editor-container">
|
||||
<svg
|
||||
id="rack-svg"
|
||||
viewBox="0 0 200 1000"
|
||||
width="100%"
|
||||
height="600"
|
||||
>
|
||||
<!-- TODO: Rack-SVG laden -->
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<p class="hint">
|
||||
Geräte per Drag & Drop im Rack positionieren.
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
<!-- =========================
|
||||
Aktionen
|
||||
========================= -->
|
||||
|
||||
<fieldset>
|
||||
<button type="submit">Speichern</button>
|
||||
<button type="button" onclick="history.back()">Abbrechen</button>
|
||||
<!-- TODO: Löschen, falls edit -->
|
||||
</fieldset>
|
||||
|
||||
</form>
|
||||
|
||||
<!-- =========================
|
||||
JS-Konfiguration
|
||||
========================= -->
|
||||
|
||||
<script>
|
||||
/**
|
||||
* Konfiguration für Rack-SVG-Editor
|
||||
*/
|
||||
|
||||
// TODO: Rack-ID aus PHP setzen
|
||||
// window.RACK_ID = <?= (int)$rackId ?>;
|
||||
|
||||
// TODO: Gerätepositionen an JS übergeben
|
||||
// window.RACK_DEVICES = <?= json_encode($rackDevices ?? []) ?>;
|
||||
</script>
|
||||
|
||||
@@ -2,44 +2,30 @@
|
||||
/**
|
||||
* app/modules/racks/list.php
|
||||
*
|
||||
* Übersicht aller Racks
|
||||
* - Anzeigen, Bearbeiten, Löschen
|
||||
* - Zugehöriges Stockwerk anzeigen
|
||||
* - Gerätecount
|
||||
* Uebersicht aller Racks.
|
||||
*/
|
||||
|
||||
// =========================
|
||||
// Filter einlesen
|
||||
// =========================
|
||||
$search = trim($_GET['search'] ?? '');
|
||||
$search = trim((string)($_GET['search'] ?? ''));
|
||||
$floorId = (int)($_GET['floor_id'] ?? 0);
|
||||
|
||||
//TODO racks beim editieren auf der stockwerkkarte platzieren und verschieben können
|
||||
|
||||
// =========================
|
||||
// WHERE-Clause bauen
|
||||
// =========================
|
||||
$where = [];
|
||||
$types = '';
|
||||
$params = [];
|
||||
|
||||
if ($search !== '') {
|
||||
$where[] = "r.name LIKE ?";
|
||||
$types .= "s";
|
||||
$where[] = 'r.name LIKE ?';
|
||||
$types .= 's';
|
||||
$params[] = "%$search%";
|
||||
}
|
||||
|
||||
if ($floorId > 0) {
|
||||
$where[] = "r.floor_id = ?";
|
||||
$types .= "i";
|
||||
$where[] = 'r.floor_id = ?';
|
||||
$types .= 'i';
|
||||
$params[] = $floorId;
|
||||
}
|
||||
|
||||
$whereSql = $where ? "WHERE " . implode(" AND ", $where) : "";
|
||||
$whereSql = $where ? ('WHERE ' . implode(' AND ', $where)) : '';
|
||||
|
||||
// =========================
|
||||
// Racks laden
|
||||
// =========================
|
||||
$racks = $sql->get(
|
||||
"SELECT r.*, f.name AS floor_name, COUNT(d.id) AS device_count
|
||||
FROM racks r
|
||||
@@ -52,212 +38,111 @@ $racks = $sql->get(
|
||||
$params
|
||||
);
|
||||
|
||||
// =========================
|
||||
// Filter-Daten laden
|
||||
// =========================
|
||||
$floors = $sql->get("SELECT id, name FROM floors ORDER BY name", "", []);
|
||||
|
||||
$floors = $sql->get('SELECT id, name FROM floors ORDER BY name', '', []);
|
||||
?>
|
||||
|
||||
<div class="racks-container">
|
||||
<h1>Racks</h1>
|
||||
|
||||
<!-- =========================
|
||||
Toolbar
|
||||
========================= -->
|
||||
<div class="filter-form">
|
||||
<form method="GET" style="display: flex; gap: 10px; flex-wrap: wrap; align-items: center;">
|
||||
<form method="GET" style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;">
|
||||
<input type="hidden" name="module" value="racks">
|
||||
<input type="hidden" name="action" value="list">
|
||||
|
||||
<input type="text" name="search" placeholder="Suche nach Name…"
|
||||
value="<?php echo htmlspecialchars($search); ?>" class="search-input">
|
||||
<input type="text" name="search" placeholder="Suche nach Name..." value="<?php echo htmlspecialchars($search); ?>" class="search-input">
|
||||
|
||||
<select name="floor_id">
|
||||
<option value="">- Alle Stockwerke -</option>
|
||||
<?php foreach ($floors as $floor): ?>
|
||||
<option value="<?php echo $floor['id']; ?>"
|
||||
<?php echo $floor['id'] === $floorId ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($floor['name']); ?>
|
||||
</option>
|
||||
<option value="<?php echo (int)$floor['id']; ?>" <?php echo ((int)$floor['id'] === $floorId) ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars((string)$floor['name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<button type="submit" class="button">Filter</button>
|
||||
<a href="?module=racks&action=list" class="button">Reset</a>
|
||||
<a href="?module=racks&action=edit" class="button button-primary" style="margin-left: auto;">+ Neues Rack</a>
|
||||
<a href="?module=racks&action=edit" class="button button-primary" style="margin-left:auto;">+ Neues Rack</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- =========================
|
||||
Racks-Tabelle
|
||||
========================= -->
|
||||
<?php if (!empty($racks)): ?>
|
||||
<table class="rack-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Stockwerk</th>
|
||||
<th>Höhe (HE)</th>
|
||||
<th>Geräte</th>
|
||||
<th>Beschreibung</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($racks as $rack): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?php echo htmlspecialchars($rack['name']); ?></strong>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<?php echo htmlspecialchars($rack['floor_name'] ?? '—'); ?>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<?php echo $rack['height_he']; ?> HE
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<?php echo $rack['device_count']; ?>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<small><?php echo htmlspecialchars($rack['comment'] ?? ''); ?></small>
|
||||
</td>
|
||||
|
||||
<td class="actions">
|
||||
<a href="?module=racks&action=edit&id=<?php echo $rack['id']; ?>" class="button button-small">Bearbeiten</a>
|
||||
<a href="#" class="button button-small button-danger" onclick="confirmDelete(<?php echo $rack['id']; ?>)">Löschen</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="rack-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Stockwerk</th>
|
||||
<th>Hoehe (HE)</th>
|
||||
<th>Geraete</th>
|
||||
<th>Beschreibung</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($racks as $rack): ?>
|
||||
<tr>
|
||||
<td><strong><?php echo htmlspecialchars((string)$rack['name']); ?></strong></td>
|
||||
<td><?php echo htmlspecialchars((string)($rack['floor_name'] ?? '-')); ?></td>
|
||||
<td><?php echo (int)$rack['height_he']; ?> HE</td>
|
||||
<td><?php echo (int)$rack['device_count']; ?></td>
|
||||
<td><small><?php echo htmlspecialchars((string)($rack['comment'] ?? '')); ?></small></td>
|
||||
<td class="actions">
|
||||
<a href="?module=racks&action=edit&id=<?php echo (int)$rack['id']; ?>" class="button button-small">Bearbeiten</a>
|
||||
<a href="#" class="button button-small button-danger" onclick="return confirmDelete(<?php echo (int)$rack['id']; ?>)">Loeschen</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php else: ?>
|
||||
<div class="empty-state">
|
||||
<p>Keine Racks gefunden.</p>
|
||||
<p>
|
||||
<a href="?module=racks&action=edit" class="button button-primary">
|
||||
Erstes Rack anlegen
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="empty-state">
|
||||
<p>Keine Racks gefunden.</p>
|
||||
<p><a href="?module=racks&action=edit" class="button button-primary">Erstes Rack anlegen</a></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.racks-container {
|
||||
padding: 20px;
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.filter-form {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.filter-form form {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-form input,
|
||||
.filter-form select {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
.rack-list {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.rack-list th {
|
||||
background: #f5f5f5;
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 2px solid #ddd;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.rack-list td {
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.rack-list tr:hover {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
|
||||
.actions {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: 8px 12px;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: #0056b3;
|
||||
}
|
||||
|
||||
.button-primary {
|
||||
background: #28a745;
|
||||
}
|
||||
|
||||
.button-primary:hover {
|
||||
background: #218838;
|
||||
}
|
||||
|
||||
.button-small {
|
||||
padding: 4px 8px;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.button-danger {
|
||||
background: #dc3545;
|
||||
}
|
||||
|
||||
.button-danger:hover {
|
||||
background: #c82333;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
background: #f9f9f9;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.racks-container { padding: 20px; max-width: 1000px; margin: 0 auto; }
|
||||
.filter-form { margin: 20px 0; }
|
||||
.filter-form input, .filter-form select { padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; }
|
||||
.search-input { flex: 1; min-width: 250px; }
|
||||
.rack-list { width: 100%; border-collapse: collapse; margin: 15px 0; }
|
||||
.rack-list th { background: #f5f5f5; padding: 12px; text-align: left; border-bottom: 2px solid #ddd; font-weight: bold; }
|
||||
.rack-list td { padding: 12px; border-bottom: 1px solid #ddd; }
|
||||
.rack-list tr:hover { background: #f9f9f9; }
|
||||
.actions { white-space: nowrap; }
|
||||
.button { display: inline-block; padding: 8px 12px; background: #007bff; color: #fff; text-decoration: none; border-radius: 4px; border: none; cursor: pointer; font-size: 0.9em; }
|
||||
.button:hover { background: #0056b3; }
|
||||
.button-primary { background: #28a745; }
|
||||
.button-primary:hover { background: #218838; }
|
||||
.button-small { padding: 4px 8px; font-size: 0.85em; }
|
||||
.button-danger { background: #dc3545; }
|
||||
.button-danger:hover { background: #c82333; }
|
||||
.empty-state { text-align: center; padding: 40px 20px; background: #f9f9f9; border: 1px solid #eee; border-radius: 8px; }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function confirmDelete(id) {
|
||||
if (confirm('Dieses Rack wirklich löschen?')) {
|
||||
// TODO: AJAX-Delete implementieren
|
||||
alert('Löschen noch nicht implementiert');
|
||||
if (!confirm('Dieses Rack wirklich loeschen?')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fetch('?module=racks&action=delete', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
|
||||
body: 'id=' + encodeURIComponent(id)
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (data && data.success) {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
alert((data && data.message) ? data.message : 'Loeschen fehlgeschlagen');
|
||||
})
|
||||
.catch(() => alert('Loeschen fehlgeschlagen'));
|
||||
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
<?php /* endif; */ ?>
|
||||
|
||||
Reference in New Issue
Block a user