PWA Mobile App für Schaltschrank-Dokumentation vor Ort: - Token-basierte Authentifizierung (15 Tage gültig) - Kundensuche mit Offline-Cache - Anlagen-Auswahl und Offline-Laden - Felder/Hutschienen/Automaten erfassen - Automatische Synchronisierung wenn wieder online - Installierbar auf dem Smartphone Home Screen - Touch-optimiertes Dark Mode Design - Quick-Select für Automaten-Werte (B16, C32, etc.) Schaltplan-Editor Verbesserungen: - Block Hover-Tooltip mit show_in_hover Feldern - Produktinfo mit Icon im Tooltip - Position und Breite in TE Neue Dateien: - pwa.php, pwa_auth.php - PWA Einstieg & Auth - ajax/pwa_api.php - PWA AJAX API - js/pwa.js, css/pwa.css - PWA App & Styles - sw.js, manifest.json - Service Worker & Manifest - img/pwa-icon-192.png, img/pwa-icon-512.png Version: 5.2.0 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
160 lines
7.7 KiB
PHP
Executable file
160 lines
7.7 KiB
PHP
Executable file
<?php
|
|
/* Copyright (C) 2026 Alles Watt lauft
|
|
*
|
|
* Gemeinsame Graph-Ansicht Funktionen für anlagen.php und contact_anlagen.php
|
|
* Vermeidet doppelten Code für Toolbar, Container, Kontextmenü, Legende
|
|
*/
|
|
|
|
/**
|
|
* Graph-spezifische JS/CSS-Dateien zu den Asset-Arrays hinzufügen
|
|
*
|
|
* @param array $jsFiles Referenz auf JS-Array
|
|
* @param array $cssFiles Referenz auf CSS-Array
|
|
* @param string $viewMode 'tree' oder 'graph'
|
|
*/
|
|
function kundenkarte_graph_add_assets(&$jsFiles, &$cssFiles, $viewMode)
|
|
{
|
|
if ($viewMode === 'graph') {
|
|
$jsFiles[] = '/kundenkarte/js/dagre.min.js';
|
|
$jsFiles[] = '/kundenkarte/js/cytoscape.min.js';
|
|
$jsFiles[] = '/kundenkarte/js/cytoscape-dagre.js';
|
|
$jsFiles[] = '/kundenkarte/js/kundenkarte_cytoscape.js?v='.time();
|
|
$cssFiles[] = '/kundenkarte/css/kundenkarte_cytoscape.css?v='.time();
|
|
} else {
|
|
array_unshift($jsFiles, '/kundenkarte/js/pathfinding.min.js');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Graph-Toolbar rendern (2 Zeilen: Aktionen + Steuerung)
|
|
* Wird UNTER der System-Tab-Borderlinie ausgegeben
|
|
*
|
|
* @param array $params Konfiguration:
|
|
* 'socid' int Kunden-ID
|
|
* 'contactid' int Kontakt-ID (0 für Kundenansicht)
|
|
* 'systemid' int Aktuelles System
|
|
* 'viewMode' string 'tree' oder 'graph'
|
|
* 'permissiontoadd' bool Schreibberechtigung
|
|
* 'pageUrl' string Aktuelle Seiten-URL ($_SERVER['PHP_SELF'])
|
|
*/
|
|
function kundenkarte_graph_print_toolbar($params)
|
|
{
|
|
global $langs;
|
|
|
|
$socId = (int) ($params['socid'] ?? 0);
|
|
$contactId = (int) ($params['contactid'] ?? 0);
|
|
$systemId = (int) ($params['systemid'] ?? 0);
|
|
$viewMode = $params['viewMode'] ?? 'tree';
|
|
$permissiontoadd = !empty($params['permissiontoadd']);
|
|
$pageUrl = $params['pageUrl'] ?? $_SERVER['PHP_SELF'];
|
|
|
|
// View-Toggle URL: ID-Parameter je nach Kontext
|
|
$idParam = ($contactId > 0) ? $contactId : $socId;
|
|
$toggleView = ($viewMode === 'graph') ? 'tree' : 'graph';
|
|
$toggleUrl = $pageUrl.'?id='.$idParam.'&system='.$systemId.'&view='.$toggleView;
|
|
$toggleIcon = ($viewMode === 'graph') ? 'fa-list' : 'fa-sitemap';
|
|
$toggleLabel = ($viewMode === 'graph') ? $langs->trans('TreeView') : $langs->trans('GraphView');
|
|
|
|
// Connection-URL: bei Kontakten wird socid + contactid übergeben
|
|
$connUrlParams = 'socid='.$socId;
|
|
if ($contactId > 0) {
|
|
$connUrlParams .= '&contactid='.$contactId;
|
|
}
|
|
|
|
print '<div class="kundenkarte-graph-toolbar">';
|
|
|
|
// Zeile 1: Ansicht-Wechsel + Aktionen
|
|
print '<div class="kundenkarte-graph-toolbar-row">';
|
|
print '<a class="button small" href="'.$toggleUrl.'" title="'.$toggleLabel.'"><i class="fa '.$toggleIcon.'"></i> '.$toggleLabel.'</a>';
|
|
if ($permissiontoadd) {
|
|
print '<a class="button small" href="'.$pageUrl.'?id='.$idParam.'&action=create&system='.$systemId.'"><i class="fa fa-plus"></i> '.$langs->trans('AddElement').'</a>';
|
|
print '<a class="button small" href="'.dol_buildpath('/kundenkarte/anlage_connection.php', 1).'?'.$connUrlParams.'&system_id='.$systemId.'&action=create"><i class="fa fa-plug"></i> '.$langs->trans('AddConnection').'</a>';
|
|
}
|
|
print '</div>';
|
|
|
|
// Zeile 2: Graph-Steuerung (Anordnen rechts)
|
|
print '<div class="kundenkarte-graph-toolbar-row">';
|
|
print '<button type="button" class="button small" id="btn-graph-reset-layout" title="Layout zurücksetzen"><i class="fa fa-refresh"></i> Layout</button>';
|
|
print '<button type="button" class="button small" id="btn-graph-wheel-zoom" title="Mausrad-Zoom"><i class="fa fa-arrows"></i> Scroll</button>';
|
|
print '<button type="button" class="button small" id="btn-graph-zoom-in" title="Vergrößern"><i class="fa fa-search-plus"></i></button>';
|
|
print '<button type="button" class="button small" id="btn-graph-zoom-out" title="Verkleinern"><i class="fa fa-search-minus"></i></button>';
|
|
print '<button type="button" class="button small" id="btn-graph-fit" title="Einpassen"><i class="fa fa-crosshairs"></i> Fit</button>';
|
|
print '<button type="button" class="button small" id="btn-graph-export-png" title="PNG exportieren"><i class="fa fa-download"></i> PNG</button>';
|
|
if ($permissiontoadd) {
|
|
print '<span class="kundenkarte-graph-toolbar-spacer"></span>';
|
|
print '<button type="button" class="button small" id="btn-graph-edit-mode" title="Elemente anordnen"><i class="fa fa-hand-paper-o"></i> Anordnen</button>';
|
|
print '<button type="button" class="button small btn-graph-save" id="btn-graph-save-positions" title="Positionen speichern" style="display:none;"><i class="fa fa-check"></i> Speichern</button>';
|
|
print '<button type="button" class="button small btn-graph-cancel" id="btn-graph-cancel-edit" title="Abbrechen" style="display:none;"><i class="fa fa-times"></i> Abbrechen</button>';
|
|
}
|
|
print '</div>';
|
|
|
|
print '</div>';
|
|
}
|
|
|
|
/**
|
|
* Graph-Container, Kontextmenü und Legende rendern
|
|
*
|
|
* @param array $params Konfiguration:
|
|
* 'socid' int Kunden-ID
|
|
* 'contactid' int Kontakt-ID (0 für Kundenansicht)
|
|
* 'systemid' int Aktuelles System
|
|
* 'permissiontoadd' bool Schreibberechtigung
|
|
* 'permissiontodelete' bool Löschberechtigung
|
|
* 'pageUrl' string Aktuelle Seiten-URL
|
|
*/
|
|
function kundenkarte_graph_print_container($params)
|
|
{
|
|
global $langs;
|
|
|
|
$socId = (int) ($params['socid'] ?? 0);
|
|
$contactId = (int) ($params['contactid'] ?? 0);
|
|
$systemId = (int) ($params['systemid'] ?? 0);
|
|
$permissiontoadd = !empty($params['permissiontoadd']);
|
|
$permissiontodelete = !empty($params['permissiontodelete']);
|
|
$pageUrl = $params['pageUrl'] ?? $_SERVER['PHP_SELF'];
|
|
|
|
$graphAjaxUrl = dol_buildpath('/kundenkarte/ajax/graph_data.php', 1);
|
|
$graphSaveUrl = dol_buildpath('/kundenkarte/ajax/graph_save_positions.php', 1);
|
|
$graphModuleUrl = dol_buildpath('/kundenkarte', 1);
|
|
|
|
print '<div class="kundenkarte-graph-wrapper">';
|
|
|
|
// Suchfeld als Overlay
|
|
print '<div class="kundenkarte-graph-search-floating">';
|
|
print '<input type="text" id="kundenkarte-graph-search" placeholder="'.$langs->trans('SearchPlaceholder').'" autocomplete="off">';
|
|
print '</div>';
|
|
|
|
// Graph-Container mit Data-Attributen für JS-Initialisierung
|
|
print '<div id="kundenkarte-graph-container"';
|
|
print ' data-ajax-url="'.dol_escape_htmltag($graphAjaxUrl).'"';
|
|
print ' data-save-url="'.dol_escape_htmltag($graphSaveUrl).'"';
|
|
print ' data-module-url="'.dol_escape_htmltag($graphModuleUrl).'"';
|
|
print ' data-socid="'.$socId.'"';
|
|
if ($contactId > 0) {
|
|
print ' data-contactid="'.$contactId.'"';
|
|
}
|
|
print ' data-systemid="'.$systemId.'"';
|
|
print ' data-can-edit="'.($permissiontoadd ? '1' : '0').'"';
|
|
print ' data-can-delete="'.($permissiontodelete ? '1' : '0').'"';
|
|
print ' data-page-url="'.dol_escape_htmltag($pageUrl).'"';
|
|
print '>';
|
|
print '<div class="kundenkarte-graph-loading"><i class="fa fa-spinner fa-spin"></i> '.$langs->trans('GraphLoading').'</div>';
|
|
print '</div>';
|
|
|
|
// Kontextmenü (Rechtsklick auf Node)
|
|
print '<div id="kundenkarte-graph-contextmenu" class="kundenkarte-graph-contextmenu" style="display:none;">';
|
|
print '<a class="ctx-item ctx-view" data-action="view"><i class="fa fa-eye"></i> '.$langs->trans('View').'</a>';
|
|
if ($permissiontoadd) {
|
|
print '<a class="ctx-item ctx-add-child" data-action="add-child"><i class="fa fa-plus"></i> '.$langs->trans('AddChild').'</a>';
|
|
print '<a class="ctx-item ctx-edit" data-action="edit"><i class="fa fa-edit"></i> '.$langs->trans('Edit').'</a>';
|
|
print '<a class="ctx-item ctx-copy" data-action="copy"><i class="fa fa-copy"></i> '.$langs->trans('Copy').'</a>';
|
|
}
|
|
if ($permissiontodelete) {
|
|
print '<a class="ctx-item ctx-delete" data-action="delete"><i class="fa fa-trash"></i> '.$langs->trans('Delete').'</a>';
|
|
}
|
|
print '</div>';
|
|
|
|
// Legende - wird dynamisch vom JS befüllt (Kabeltypen mit Farben)
|
|
print '<div id="kundenkarte-graph-legend" class="kundenkarte-graph-legend"></div>';
|
|
print '</div>'; // End kundenkarte-graph-wrapper
|
|
}
|