Verhalten im Infrastruktur-Modul ist jetzt wie gewünscht:
Nur das aktiv bearbeitete Objekt ist verschiebbar. Alle anderen Objekte auf derselben Etage werden als halbtransparente Referenz angezeigt. In der Listenansicht sind Marker nur noch Vorschau (nicht verschiebbar). Geänderte Dateien: edit.php Referenzdaten für Patchpanels und Wandbuchsen geladen. Diese Daten als data-* am Canvas hinterlegt. Hinweise im UI angepasst. Styles ergänzt: .floor-plan-marker.is-active .floor-plan-reference (+ Varianten für Panel/Outlet) Inline-Script entfernt und auf externe JS-Datei umgestellt (CSP-sicher): <script src="/assets/js/floor-infrastructure-edit.js" defer></script> floor-infrastructure-edit.js (neu) Drag/Drop nur für den aktiven Marker. Referenzmarker je Stockwerk rendern (halbtransparent, nicht interaktiv). Aktiven Marker aus Referenzmenge ausschließen. Referenzen bei Stockwerk-/Raumwechsel neu rendern. list.php Kartenansicht auf reine Vorschau umgestellt. Drag/Save-Logik entfernt. Marker per CSS nicht interaktiv (pointer-events: none, cursor: default). Hinweistext entsprechend angepasst.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
/**
|
||||
* app/modules/floor_infrastructure/list.php
|
||||
*
|
||||
* Uebersicht ueber Patchpanels und Netzwerkbuchsen auf Stockwerken
|
||||
* Übersicht über Patchpanels und Netzwerkbuchsen auf Stockwerken
|
||||
*/
|
||||
|
||||
$floorId = (int)($_GET['floor_id'] ?? 0);
|
||||
@@ -101,10 +101,10 @@ if ($editorFloorId > 0) {
|
||||
|
||||
<div class="toolbar">
|
||||
<a href="?module=floor_infrastructure&action=edit&type=patchpanel" class="button button-primary">
|
||||
+ Patchpanel hinzufuegen
|
||||
+ Patchpanel hinzufügen
|
||||
</a>
|
||||
<a href="?module=floor_infrastructure&action=edit&type=outlet" class="button">
|
||||
+ Wandbuchse hinzufuegen
|
||||
+ Wandbuchse hinzufügen
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -122,7 +122,7 @@ if ($editorFloorId > 0) {
|
||||
</select>
|
||||
|
||||
<button class="button">Filter</button>
|
||||
<a href="?module=floor_infrastructure&action=list" class="button">Zuruecksetzen</a>
|
||||
<a href="?module=floor_infrastructure&action=list" class="button">Zurücksetzen</a>
|
||||
</form>
|
||||
|
||||
<section class="infra-section">
|
||||
@@ -134,7 +134,7 @@ if ($editorFloorId > 0) {
|
||||
<th>Name</th>
|
||||
<th>Stockwerk</th>
|
||||
<th>Position</th>
|
||||
<th>Groesse</th>
|
||||
<th>Größe</th>
|
||||
<th>Ports</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
@@ -196,18 +196,18 @@ if ($editorFloorId > 0) {
|
||||
<section class="infra-plan">
|
||||
<h2>Stockwerksplan-Verortung</h2>
|
||||
<?php if (!$editorFloor): ?>
|
||||
<p class="empty-state">Kein Stockwerk fuer die Planansicht verfuegbar.</p>
|
||||
<p class="empty-state">Kein Stockwerk für die Planansicht verfügbar.</p>
|
||||
<?php elseif (($editorFloor['svg_url'] ?? '') === ''): ?>
|
||||
<p class="empty-state">Das gewaehlte Stockwerk hat noch keinen SVG-Plan hinterlegt.</p>
|
||||
<p class="empty-state">Das gewählte Stockwerk hat noch keinen SVG-Plan hinterlegt.</p>
|
||||
<?php else: ?>
|
||||
<p>Drag & Drop ist aktiv fuer <strong><?php echo htmlspecialchars($editorFloor['name']); ?></strong>. Aenderungen werden direkt gespeichert.</p>
|
||||
<p>Vorschau für <strong><?php echo htmlspecialchars($editorFloor['name']); ?></strong>. Positionen bearbeitest du über den jeweiligen „Bearbeiten“-Button.</p>
|
||||
<div id="infra-floor-canvas"
|
||||
class="infra-floor-canvas"
|
||||
data-save-url="?module=floor_infrastructure&action=save"
|
||||
data-floor-id="<?php echo (int)$editorFloor['id']; ?>">
|
||||
<img src="<?php echo htmlspecialchars($editorFloor['svg_url']); ?>" class="infra-floor-svg" alt="Stockwerksplan">
|
||||
</div>
|
||||
<p class="floor-plan-hint">Patchpanels: blau, Wandbuchsen: gruen. Ziehen und loslassen zum Speichern.</p>
|
||||
<p class="floor-plan-hint">Patchpanels: blau, Wandbuchsen: grün. In dieser Ansicht sind Marker nicht verschiebbar.</p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</div>
|
||||
@@ -277,8 +277,9 @@ if ($editorFloorId > 0) {
|
||||
left: 0;
|
||||
user-select: none;
|
||||
touch-action: none;
|
||||
cursor: move;
|
||||
cursor: default;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
}
|
||||
.infra-marker.patchpanel {
|
||||
background: rgba(13, 110, 253, 0.25);
|
||||
@@ -292,9 +293,6 @@ if ($editorFloorId > 0) {
|
||||
border: 2px solid #198754;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.infra-marker.is-saving {
|
||||
opacity: 0.65;
|
||||
}
|
||||
.floor-plan-hint {
|
||||
margin: 8px 0 0;
|
||||
font-size: 0.85em;
|
||||
@@ -319,95 +317,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const saveUrl = canvas.dataset.saveUrl;
|
||||
const floorId = Number(canvas.dataset.floorId || 0);
|
||||
const outletSize = 32;
|
||||
const patchPanels = <?php echo json_encode($editorPatchPanels, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?>;
|
||||
const outlets = <?php echo json_encode($editorOutlets, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?>;
|
||||
|
||||
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
||||
|
||||
const savePosition = async (entry, type, marker) => {
|
||||
const form = new FormData();
|
||||
|
||||
if (type === 'patchpanel') {
|
||||
form.append('type', 'patchpanel');
|
||||
form.append('id', String(entry.id));
|
||||
form.append('name', entry.name || '');
|
||||
form.append('floor_id', String(floorId));
|
||||
form.append('pos_x', String(entry.x));
|
||||
form.append('pos_y', String(entry.y));
|
||||
form.append('width', String(entry.width));
|
||||
form.append('height', String(entry.height));
|
||||
form.append('port_count', String(entry.port_count || 0));
|
||||
form.append('comment', entry.comment || '');
|
||||
} else {
|
||||
form.append('type', 'outlet');
|
||||
form.append('id', String(entry.id));
|
||||
form.append('name', entry.name || '');
|
||||
form.append('room_id', String(entry.room_id || 0));
|
||||
form.append('x', String(entry.x));
|
||||
form.append('y', String(entry.y));
|
||||
form.append('comment', entry.comment || '');
|
||||
}
|
||||
|
||||
marker.classList.add('is-saving');
|
||||
try {
|
||||
await fetch(saveUrl, {
|
||||
method: 'POST',
|
||||
body: form,
|
||||
credentials: 'same-origin'
|
||||
});
|
||||
} finally {
|
||||
marker.classList.remove('is-saving');
|
||||
}
|
||||
};
|
||||
|
||||
const bindDrag = (marker, entry, type) => {
|
||||
let dragging = false;
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
|
||||
marker.addEventListener('pointerdown', (event) => {
|
||||
dragging = true;
|
||||
const rect = marker.getBoundingClientRect();
|
||||
offsetX = event.clientX - rect.left;
|
||||
offsetY = event.clientY - rect.top;
|
||||
marker.setPointerCapture(event.pointerId);
|
||||
});
|
||||
|
||||
marker.addEventListener('pointermove', (event) => {
|
||||
if (!dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const width = type === 'patchpanel' ? entry.width : outletSize;
|
||||
const height = type === 'patchpanel' ? entry.height : outletSize;
|
||||
const maxX = Math.max(0, rect.width - width);
|
||||
const maxY = Math.max(0, rect.height - height);
|
||||
|
||||
entry.x = Math.round(clamp(event.clientX - rect.left - offsetX, 0, maxX));
|
||||
entry.y = Math.round(clamp(event.clientY - rect.top - offsetY, 0, maxY));
|
||||
marker.style.left = `${entry.x}px`;
|
||||
marker.style.top = `${entry.y}px`;
|
||||
});
|
||||
|
||||
const stopDrag = (event) => {
|
||||
if (!dragging) {
|
||||
return;
|
||||
}
|
||||
dragging = false;
|
||||
if (marker.hasPointerCapture(event.pointerId)) {
|
||||
marker.releasePointerCapture(event.pointerId);
|
||||
}
|
||||
savePosition(entry, type, marker);
|
||||
};
|
||||
|
||||
marker.addEventListener('pointerup', stopDrag);
|
||||
marker.addEventListener('pointercancel', stopDrag);
|
||||
};
|
||||
|
||||
const createMarker = (entry, type) => {
|
||||
const marker = document.createElement('div');
|
||||
marker.className = `infra-marker ${type}`;
|
||||
@@ -423,8 +336,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
marker.style.left = `${entry.x}px`;
|
||||
marker.style.top = `${entry.y}px`;
|
||||
canvas.appendChild(marker);
|
||||
|
||||
bindDrag(marker, entry, type);
|
||||
};
|
||||
|
||||
patchPanels.forEach((entry) => createMarker(entry, 'patchpanel'));
|
||||
|
||||
Reference in New Issue
Block a user