feat(schematic): Junction-Verbindungen und Werte-Übernahme

- Terminal→Leitung Verbindungen: Klick auf Leitung im Zeichenmodus
  erstellt Verbindung zum Ziel-Equipment der angeklickten Leitung
- Dialog übernimmt Werte (Typ, Farbe, Kabel) von der Ziel-Leitung
- Zeichenmodus bleibt aktiv nach Verbindungserstellung
- Neue Phasen-Farben: LN (braun), DATA (lila)
- Debug-Logging per Flag steuerbar (DEBUG: false)
- Layout-Optimierungen: Routing-Zone, Output-Zone Höhen angepasst

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eduard Wisch 2026-03-04 11:55:19 +01:00
parent 89a4db4d21
commit 69dc7e8728
12 changed files with 2311 additions and 158 deletions

View file

@ -44,8 +44,11 @@ switch ($action) {
'id' => $connection->id, 'id' => $connection->id,
'fk_source' => $connection->fk_source, 'fk_source' => $connection->fk_source,
'source_terminal' => $connection->source_terminal, 'source_terminal' => $connection->source_terminal,
'source_terminal_id' => $connection->source_terminal_id,
'bundled_terminals' => $connection->bundled_terminals,
'fk_target' => $connection->fk_target, 'fk_target' => $connection->fk_target,
'target_terminal' => $connection->target_terminal, 'target_terminal' => $connection->target_terminal,
'target_terminal_id' => $connection->target_terminal_id,
'connection_type' => $connection->connection_type, 'connection_type' => $connection->connection_type,
'color' => $connection->color, 'color' => $connection->color,
'output_label' => $connection->output_label, 'output_label' => $connection->output_label,
@ -77,11 +80,14 @@ switch ($action) {
'id' => $c->id, 'id' => $c->id,
'fk_source' => $c->fk_source, 'fk_source' => $c->fk_source,
'source_terminal' => $c->source_terminal, 'source_terminal' => $c->source_terminal,
'source_terminal_id' => $c->source_terminal_id,
'bundled_terminals' => $c->bundled_terminals,
'source_label' => $c->source_label, 'source_label' => $c->source_label,
'source_pos' => $c->source_pos, 'source_pos' => $c->source_pos,
'source_width' => $c->source_width, 'source_width' => $c->source_width,
'fk_target' => $c->fk_target, 'fk_target' => $c->fk_target,
'target_terminal' => $c->target_terminal, 'target_terminal' => $c->target_terminal,
'target_terminal_id' => $c->target_terminal_id,
'target_label' => $c->target_label, 'target_label' => $c->target_label,
'target_pos' => $c->target_pos, 'target_pos' => $c->target_pos,
'connection_type' => $c->connection_type, 'connection_type' => $c->connection_type,
@ -154,6 +160,7 @@ switch ($action) {
$connection->fk_carrier = $carrierId; $connection->fk_carrier = $carrierId;
$connection->position_y = GETPOSTINT('position_y'); $connection->position_y = GETPOSTINT('position_y');
$connection->path_data = GETPOST('path_data', 'nohtml'); $connection->path_data = GETPOST('path_data', 'nohtml');
$connection->bundled_terminals = GETPOST('bundled_terminals', 'alphanohtml');
$result = $connection->create($user); $result = $connection->create($user);
if ($result > 0) { if ($result > 0) {
@ -188,6 +195,7 @@ switch ($action) {
if (GETPOSTISSET('excluded_te')) $connection->excluded_te = GETPOST('excluded_te', 'alphanohtml'); if (GETPOSTISSET('excluded_te')) $connection->excluded_te = GETPOST('excluded_te', 'alphanohtml');
if (GETPOSTISSET('position_y')) $connection->position_y = GETPOSTINT('position_y'); if (GETPOSTISSET('position_y')) $connection->position_y = GETPOSTINT('position_y');
if (GETPOSTISSET('path_data')) $connection->path_data = GETPOST('path_data', 'nohtml'); if (GETPOSTISSET('path_data')) $connection->path_data = GETPOST('path_data', 'nohtml');
if (GETPOSTISSET('bundled_terminals')) $connection->bundled_terminals = GETPOST('bundled_terminals', 'alphanohtml');
$result = $connection->update($user); $result = $connection->update($user);
if ($result > 0) { if ($result > 0) {
@ -341,6 +349,7 @@ switch ($action) {
'fk_source' => $obj->fk_source, 'fk_source' => $obj->fk_source,
'source_terminal' => $obj->source_terminal, 'source_terminal' => $obj->source_terminal,
'source_terminal_id' => $obj->source_terminal_id, 'source_terminal_id' => $obj->source_terminal_id,
'bundled_terminals' => isset($obj->bundled_terminals) ? $obj->bundled_terminals : null,
'source_label' => $obj->source_label, 'source_label' => $obj->source_label,
'source_pos' => $obj->source_pos, 'source_pos' => $obj->source_pos,
'source_width' => $obj->source_width, 'source_width' => $obj->source_width,

View file

@ -76,7 +76,7 @@ class modKundenKarte extends DolibarrModules
$this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@kundenkarte' $this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@kundenkarte'
// Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z' // Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z'
$this->version = '8.6'; $this->version = '9.1';
// Url to the file with your last numberversion of this module // Url to the file with your last numberversion of this module
//$this->url_last_version = 'http://www.example.com/versionmodule.txt'; //$this->url_last_version = 'http://www.example.com/versionmodule.txt';

View file

@ -2119,6 +2119,13 @@ body.kundenkarte-drag-active * {
margin-top: 20px !important; margin-top: 20px !important;
max-width: 100% !important; max-width: 100% !important;
overflow-x: auto !important; overflow-x: auto !important;
display: flex !important;
flex-direction: column !important;
}
/* Prevent gap between header and message */
.schematic-editor-wrapper > * {
flex-shrink: 0 !important;
} }
/* Prevent Schematic Editor from breaking Dolibarr layout */ /* Prevent Schematic Editor from breaking Dolibarr layout */
@ -2135,6 +2142,17 @@ body.kundenkarte-drag-active * {
background: #252525 !important; background: #252525 !important;
border: 1px solid #333 !important; border: 1px solid #333 !important;
border-radius: 4px 4px 0 0 !important; border-radius: 4px 4px 0 0 !important;
margin: 0 !important;
box-sizing: border-box !important;
min-height: 50px !important;
flex-wrap: wrap !important;
}
.schematic-editor-actions {
display: flex !important;
flex-wrap: wrap !important;
gap: 10px !important;
align-items: center !important;
} }
.schematic-editor-toggle { .schematic-editor-toggle {
@ -2163,6 +2181,7 @@ body.kundenkarte-drag-active * {
min-height: 300px !important; min-height: 300px !important;
max-width: 100% !important; max-width: 100% !important;
box-sizing: border-box !important; box-sizing: border-box !important;
margin: 0 !important;
} }
.schematic-editor-canvas.expanded { .schematic-editor-canvas.expanded {
@ -2260,7 +2279,7 @@ body.kundenkarte-drag-active * {
/* Messages - Fixed height status bar */ /* Messages - Fixed height status bar */
.schematic-message { .schematic-message {
padding: 6px 15px !important; padding: 6px 15px !important;
margin-bottom: 0 !important; margin: 0 !important;
border-radius: 0 !important; border-radius: 0 !important;
font-size: 12px !important; font-size: 12px !important;
height: 28px !important; height: 28px !important;
@ -2275,6 +2294,7 @@ body.kundenkarte-drag-active * {
border: 1px solid #3498db !important; border: 1px solid #3498db !important;
border-top: none !important; border-top: none !important;
border-bottom: none !important; border-bottom: none !important;
display: block !important;
} }
.schematic-message.info { .schematic-message.info {

View file

@ -1144,6 +1144,21 @@ body {
stroke-linejoin: round; stroke-linejoin: round;
} }
/* Wire Toggle Button */
#btn-toggle-wires {
opacity: 0.5;
transition: opacity 0.2s, background 0.2s;
}
#btn-toggle-wires.active {
opacity: 1;
background: rgba(173, 140, 79, 0.3);
}
#btn-toggle-wires svg {
fill: currentColor;
}
/* Grid-Rows: 1=Labels oben, 2=Terminals oben, 3=Equipment, 4=Terminals unten, 5=Labels unten */ /* Grid-Rows: 1=Labels oben, 2=Terminals oben, 3=Equipment, 4=Terminals unten, 5=Labels unten */
/* Add Button in Carrier (letzte Spalte, Zeile 2) */ /* Add Button in Carrier (letzte Spalte, Zeile 2) */
.btn-add-equipment { .btn-add-equipment {

0
img/pwa-icon-192.png Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

0
img/pwa-icon-512.png Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 129 KiB

File diff suppressed because it is too large Load diff

128
js/pwa.js
View file

@ -36,6 +36,9 @@
offlineQueue: [], offlineQueue: [],
isOnline: navigator.onLine, isOnline: navigator.onLine,
// Display settings
showConnectionLines: false, // Leitungen standardmäßig ausgeblendet
// Current modal state // Current modal state
currentCarrierId: null, currentCarrierId: null,
editCarrierId: null, // null = Add-Modus, ID = Edit-Modus (Hutschiene) editCarrierId: null, // null = Add-Modus, ID = Edit-Modus (Hutschiene)
@ -186,6 +189,7 @@
// Editor actions // Editor actions
$('#btn-add-panel').on('click', () => openModal('add-panel')); $('#btn-add-panel').on('click', () => openModal('add-panel'));
$('#btn-save-panel').on('click', handleSavePanel); $('#btn-save-panel').on('click', handleSavePanel);
$('#btn-toggle-wires').on('click', handleToggleWires);
$('#editor-content').on('click', '.btn-add-carrier', handleAddCarrier); $('#editor-content').on('click', '.btn-add-carrier', handleAddCarrier);
$('#editor-content').on('click', '.carrier-header', handleCarrierClick); $('#editor-content').on('click', '.carrier-header', handleCarrierClick);
@ -1152,14 +1156,25 @@
} }
/** /**
* Render SVG connection lines from path_data * Render SVG connection lines between equipment
* Only shows connections that were manually drawn on the website * PWA uses different layout than desktop, so we calculate positions dynamically
*/ */
function renderConnectionLines() { function renderConnectionLines() {
// Remove existing SVG overlays first
$('.connection-lines-svg').remove();
// Only render if setting is enabled
if (!App.showConnectionLines) {
return;
}
if (!App.connections || App.connections.length === 0) { if (!App.connections || App.connections.length === 0) {
return; return;
} }
// Desktop reference dimensions
const DESKTOP_TE_WIDTH = 56;
// Für jede Hutschiene ein SVG-Overlay erstellen // Für jede Hutschiene ein SVG-Overlay erstellen
$('.carrier-card').each(function() { $('.carrier-card').each(function() {
const $carrier = $(this); const $carrier = $(this);
@ -1172,6 +1187,18 @@
const carrierEquipment = App.equipment.filter(e => e.fk_carrier == carrierId); const carrierEquipment = App.equipment.filter(e => e.fk_carrier == carrierId);
const equipmentIds = carrierEquipment.map(e => e.id); const equipmentIds = carrierEquipment.map(e => e.id);
// Carrier-Daten für Total-TE
const carrier = App.carriers.find(c => c.id == carrierId);
const totalTe = carrier ? (parseInt(carrier.total_te) || 12) : 12;
// PWA TE-Breite berechnen
const carrierWidth = $content.width();
const pwaTeWidth = carrierWidth / (totalTe + 1); // +1 für den Add-Button
// Scale factor: PWA-Breite / Desktop-Breite
const scaleX = pwaTeWidth / DESKTOP_TE_WIDTH;
const scaleY = scaleX * 0.8; // Y etwas weniger skalieren (PWA ist kompakter)
// Verbindungen filtern die zu dieser Hutschiene gehören // Verbindungen filtern die zu dieser Hutschiene gehören
const carrierConnections = App.connections.filter(c => const carrierConnections = App.connections.filter(c =>
equipmentIds.includes(parseInt(c.fk_source)) || equipmentIds.includes(parseInt(c.fk_source)) ||
@ -1195,16 +1222,88 @@
const color = conn.color || getPhaseColor(conn.connection_type); const color = conn.color || getPhaseColor(conn.connection_type);
// Transform path_data coordinates to PWA scale
const scaledPath = transformPathData(conn.path_data, scaleX, scaleY);
// Schatten-Pfad für bessere Sichtbarkeit // Schatten-Pfad für bessere Sichtbarkeit
svgContent += `<path class="connection-shadow" d="${conn.path_data}" />`; svgContent += `<path class="connection-shadow" d="${scaledPath}" />`;
// Hauptpfad // Hauptpfad
svgContent += `<path class="connection-line" d="${conn.path_data}" style="stroke:${color}" data-connection-id="${conn.id}" />`; svgContent += `<path class="connection-line" d="${scaledPath}" style="stroke:${color}" data-connection-id="${conn.id}" />`;
// Label falls vorhanden
if (conn.output_label) {
const labelPos = getPathMidpoint(scaledPath);
if (labelPos) {
const labelWidth = Math.min(conn.output_label.length * 6 + 10, 80);
svgContent += `<rect x="${labelPos.x - labelWidth/2}" y="${labelPos.y - 8}" width="${labelWidth}" height="16" rx="3" fill="#1a1a1a" stroke="${color}" stroke-width="1"/>`;
svgContent += `<text x="${labelPos.x}" y="${labelPos.y + 4}" text-anchor="middle" fill="${color}" font-size="10" font-weight="bold">${escapeHtml(conn.output_label)}</text>`;
}
}
}); });
$svg.html(svgContent); $svg.html(svgContent);
}); });
} }
/**
* Transform path data coordinates by scale factors
*/
function transformPathData(pathData, scaleX, scaleY) {
if (!pathData) return '';
// Parse and transform coordinates
return pathData.replace(/([ML])\s*([\d.-]+)\s+([\d.-]+)/gi, function(match, cmd, x, y) {
const newX = (parseFloat(x) * scaleX).toFixed(1);
const newY = (parseFloat(y) * scaleY).toFixed(1);
return `${cmd} ${newX} ${newY}`;
});
}
/**
* Get midpoint of a path for label positioning
*/
function getPathMidpoint(pathData) {
if (!pathData) return null;
const points = [];
const regex = /[ML]\s*([\d.-]+)\s+([\d.-]+)/gi;
let match;
while ((match = regex.exec(pathData)) !== null) {
points.push({ x: parseFloat(match[1]), y: parseFloat(match[2]) });
}
if (points.length < 2) return null;
// Calculate midpoint along path
let totalLength = 0;
const segments = [];
for (let i = 1; i < points.length; i++) {
const dx = points[i].x - points[i-1].x;
const dy = points[i].y - points[i-1].y;
const len = Math.sqrt(dx*dx + dy*dy);
segments.push({ start: points[i-1], end: points[i], length: len });
totalLength += len;
}
const halfLength = totalLength / 2;
let accumulated = 0;
for (const seg of segments) {
if (accumulated + seg.length >= halfLength) {
const t = (halfLength - accumulated) / seg.length;
return {
x: seg.start.x + t * (seg.end.x - seg.start.x),
y: seg.start.y + t * (seg.end.y - seg.start.y)
};
}
accumulated += seg.length;
}
return { x: (points[0].x + points[points.length-1].x) / 2, y: (points[0].y + points[points.length-1].y) / 2 };
}
function renderTypeGrid() { function renderTypeGrid() {
const categoryLabels = { const categoryLabels = {
'automat': 'Leitungsschutz', 'automat': 'Leitungsschutz',
@ -1238,6 +1337,27 @@
$('#type-grid').html(html); $('#type-grid').html(html);
} }
// ============================================
// WIRE DISPLAY TOGGLE
// ============================================
function handleToggleWires() {
App.showConnectionLines = !App.showConnectionLines;
// Update button appearance
const $btn = $('#btn-toggle-wires');
if (App.showConnectionLines) {
$btn.addClass('active');
$btn.attr('title', 'Leitungen ausblenden');
} else {
$btn.removeClass('active');
$btn.attr('title', 'Leitungen einblenden');
}
// Re-render connection lines
renderConnectionLines();
}
// ============================================ // ============================================
// PANEL (FELD) ACTIONS // PANEL (FELD) ACTIONS
// ============================================ // ============================================

View file

@ -133,6 +133,9 @@ $themeColor = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#3498db');
<svg viewBox="0 0 24 24"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg> <svg viewBox="0 0 24 24"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/></svg>
<span id="sync-badge" class="sync-badge hidden">0</span> <span id="sync-badge" class="sync-badge hidden">0</span>
</button> </button>
<button id="btn-toggle-wires" class="btn-icon" title="Leitungen ein-/ausblenden">
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" class="wire-icon-off"/><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17l-4-4 1.4-1.4 2.6 2.6 6.6-6.6L17 9l-8 8z" class="wire-icon-on hidden"/></svg>
</button>
</header> </header>
<div id="editor-content" class="editor-content"> <div id="editor-content" class="editor-content">

4
sw.js
View file

@ -3,8 +3,8 @@
* Offline-First für Schaltschrank-Dokumentation * Offline-First für Schaltschrank-Dokumentation
*/ */
const CACHE_NAME = 'kundenkarte-pwa-v6.1'; const CACHE_NAME = 'kundenkarte-pwa-v9.1';
const OFFLINE_CACHE = 'kundenkarte-offline-v6.1'; const OFFLINE_CACHE = 'kundenkarte-offline-v9.1';
// Statische Assets die immer gecached werden (ohne Query-String) // Statische Assets die immer gecached werden (ohne Query-String)
const STATIC_ASSETS = [ const STATIC_ASSETS = [

View file

@ -781,6 +781,9 @@ if (empty($customerSystems)) {
print '<button type="button" class="schematic-add-busbar" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#f39c12;cursor:pointer;" title="Phasenschiene hinzufügen">'; print '<button type="button" class="schematic-add-busbar" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#f39c12;cursor:pointer;" title="Phasenschiene hinzufügen">';
print '<i class="fa fa-arrows-h"></i> Phasenschiene'; print '<i class="fa fa-arrows-h"></i> Phasenschiene';
print '</button>'; print '</button>';
print '<button type="button" class="schematic-straighten-connections" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#2ecc71;cursor:pointer;" title="Diagonale Leitungen begradigen (nur rechte Winkel)">';
print '<i class="fa fa-align-justify"></i> Begradigen';
print '</button>';
print '<button type="button" class="schematic-clear-connections" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#e74c3c;cursor:pointer;">'; print '<button type="button" class="schematic-clear-connections" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#e74c3c;cursor:pointer;">';
print '<i class="fa fa-trash"></i> Alle Verbindungen löschen'; print '<i class="fa fa-trash"></i> Alle Verbindungen löschen';
print '</button>'; print '</button>';
@ -792,6 +795,10 @@ if (empty($customerSystems)) {
print '<button type="button" class="schematic-audit-log" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#95a5a6;cursor:pointer;" title="Änderungsprotokoll anzeigen">'; print '<button type="button" class="schematic-audit-log" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#95a5a6;cursor:pointer;" title="Änderungsprotokoll anzeigen">';
print '<i class="fa fa-history"></i> Protokoll'; print '<i class="fa fa-history"></i> Protokoll';
print '</button>'; print '</button>';
// Display Settings button
print '<button type="button" class="schematic-settings-btn" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#3498db;cursor:pointer;" title="Anzeigeeinstellungen (Leitungsfarben, -stärken, Terminal-Stil)">';
print '<i class="fa fa-cog"></i> Anzeige';
print '</button>';
// PDF Export button // PDF Export button
$pdfExportUrl = dol_buildpath('/kundenkarte/ajax/export_schematic_pdf.php', 1).'?anlage_id='.$anlageId.'&format=A4&orientation=L'; $pdfExportUrl = dol_buildpath('/kundenkarte/ajax/export_schematic_pdf.php', 1).'?anlage_id='.$anlageId.'&format=A4&orientation=L';
print '<a href="'.$pdfExportUrl.'" target="_blank" class="schematic-export-pdf" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#3498db;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:5px;" title="PDF Export (Leitungslaufplan nach DIN EN 61082)">'; print '<a href="'.$pdfExportUrl.'" target="_blank" class="schematic-export-pdf" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#3498db;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:5px;" title="PDF Export (Leitungslaufplan nach DIN EN 61082)">';

View file

@ -779,6 +779,9 @@ if (empty($customerSystems)) {
print '<button type="button" class="schematic-add-busbar" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#f39c12;cursor:pointer;" title="Phasenschiene hinzufügen">'; print '<button type="button" class="schematic-add-busbar" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#f39c12;cursor:pointer;" title="Phasenschiene hinzufügen">';
print '<i class="fa fa-arrows-h"></i> Phasenschiene'; print '<i class="fa fa-arrows-h"></i> Phasenschiene';
print '</button>'; print '</button>';
print '<button type="button" class="schematic-straighten-connections" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#2ecc71;cursor:pointer;" title="Diagonale Leitungen begradigen (nur rechte Winkel)">';
print '<i class="fa fa-align-justify"></i> Begradigen';
print '</button>';
print '<button type="button" class="schematic-clear-connections" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#e74c3c;cursor:pointer;">'; print '<button type="button" class="schematic-clear-connections" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#e74c3c;cursor:pointer;">';
print '<i class="fa fa-trash"></i> Alle Verbindungen löschen'; print '<i class="fa fa-trash"></i> Alle Verbindungen löschen';
print '</button>'; print '</button>';
@ -790,6 +793,10 @@ if (empty($customerSystems)) {
print '<button type="button" class="schematic-audit-log" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#95a5a6;cursor:pointer;" title="Änderungsprotokoll anzeigen">'; print '<button type="button" class="schematic-audit-log" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#95a5a6;cursor:pointer;" title="Änderungsprotokoll anzeigen">';
print '<i class="fa fa-history"></i> Protokoll'; print '<i class="fa fa-history"></i> Protokoll';
print '</button>'; print '</button>';
// Display Settings button
print '<button type="button" class="schematic-settings-btn" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#3498db;cursor:pointer;" title="Anzeigeeinstellungen (Leitungsfarben, -stärken, Terminal-Stil)">';
print '<i class="fa fa-cog"></i> Anzeige';
print '</button>';
// PDF Export button // PDF Export button
$pdfExportUrl = dol_buildpath('/kundenkarte/ajax/export_schematic_pdf.php', 1).'?anlage_id='.$anlageId.'&format=A4&orientation=L'; $pdfExportUrl = dol_buildpath('/kundenkarte/ajax/export_schematic_pdf.php', 1).'?anlage_id='.$anlageId.'&format=A4&orientation=L';
print '<a href="'.$pdfExportUrl.'" target="_blank" class="schematic-export-pdf" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#3498db;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:5px;" title="PDF Export (Leitungslaufplan nach DIN EN 61082)">'; print '<a href="'.$pdfExportUrl.'" target="_blank" class="schematic-export-pdf" style="padding:5px 10px;background:#333;border:1px solid #555;border-radius:3px;color:#3498db;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:5px;" title="PDF Export (Leitungslaufplan nach DIN EN 61082)">';