kundenkarte/pwa.php
data 143ddcb958 feat: Graph-Toggle, Baum-Farben, PWA-Baumansicht
- Graph-Integration von feature/cytoscape-graph auf main portiert
  (anlagen.php + contact_anlagen.php: View-Mode, Toolbar, Container)
- Baum-Knoten farblich unterschieden: grün=Gebäude, blau=Equipment, orange=Endgerät
  (CSS border-left + Icon-Farbe je nach can_have_children/can_have_equipment)
- PWA: Kompletter Anlagen-Baum statt flache Liste
  (API liefert rekursiven Baum, Frontend mit aufklappbaren Knoten)
- PWA: Equipment-Container öffnen Editor, Strukturknoten klappen auf/zu
- Connection-URLs in contact_anlagen.php: contactid Parameter ergänzt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:09:13 +01:00

329 lines
11 KiB
PHP

<?php
/* Copyright (C) 2026 Alles Watt lauft
*
* KundenKarte PWA - Mobile Schaltschrank-Dokumentation
* Offline-fähig für Elektriker vor Ort
*/
// Kein Dolibarr-Login erforderlich - eigenes Token-System
if (!defined('NOLOGIN')) {
define('NOLOGIN', '1');
}
if (!defined('NOREQUIREMENU')) {
define('NOREQUIREMENU', '1');
}
if (!defined('NOREQUIREHTML')) {
define('NOREQUIREHTML', '1');
}
// Load Dolibarr environment
$res = 0;
if (!$res && file_exists("../main.inc.php")) {
$res = @include "../main.inc.php";
}
if (!$res && file_exists("../../main.inc.php")) {
$res = @include "../../main.inc.php";
}
if (!$res) {
die("Dolibarr konnte nicht geladen werden");
}
// Theme color from Dolibarr
$themeColor = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#3498db');
?><!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<meta name="theme-color" content="<?php echo $themeColor; ?>">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="KundenKarte">
<title>KundenKarte</title>
<link rel="manifest" href="manifest.json">
<link rel="icon" type="image/png" sizes="192x192" href="img/pwa-icon-192.png">
<link rel="apple-touch-icon" href="img/pwa-icon-192.png">
<link rel="stylesheet" href="css/pwa.css?v=2.8">
<style>:root { --primary: <?php echo $themeColor; ?>; }</style>
</head>
<body>
<div id="app" class="app">
<!-- Login Screen -->
<div id="screen-login" class="screen active">
<div class="login-container">
<div class="login-logo">
<svg viewBox="0 0 24 24" fill="currentColor">
<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-2zm-7 14h-2v-4H6v-2h4V7h2v4h4v2h-4v4z"/>
</svg>
</div>
<h1 class="login-title">KundenKarte</h1>
<p class="login-subtitle">Schaltschrank-Dokumentation</p>
<form id="login-form" class="login-form">
<div class="form-group">
<label>Benutzername</label>
<input type="text" id="login-user" autocomplete="username" required>
</div>
<div class="form-group">
<label>Passwort</label>
<input type="password" id="login-pass" autocomplete="current-password" required>
</div>
<button type="submit" class="btn btn-primary btn-large">
Anmelden
</button>
<p id="login-error" class="error-text"></p>
</form>
</div>
</div>
<!-- Kundensuche Screen -->
<div id="screen-search" class="screen">
<header class="header">
<h1>Kunde wählen</h1>
<button id="btn-logout" class="btn-icon" title="Abmelden">
<svg viewBox="0 0 24 24"><path d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"/></svg>
</button>
</header>
<div class="search-container">
<div class="search-box">
<svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
<input type="text" id="search-customer" placeholder="Kunde suchen...">
</div>
</div>
<div id="customer-list" class="list">
<!-- Kunden werden hier geladen -->
</div>
<div id="offline-indicator" class="offline-bar hidden">
⚡ Offline-Modus
</div>
</div>
<!-- Anlagen Screen -->
<div id="screen-anlagen" class="screen">
<header class="header">
<button id="btn-back-search" class="btn-icon">
<svg viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
</button>
<h1 id="customer-name">Kunde</h1>
<div class="header-spacer"></div>
</header>
<div id="anlagen-list" class="list anlagen-grid">
<!-- Anlagen werden hier geladen -->
</div>
</div>
<!-- Editor Screen -->
<div id="screen-editor" class="screen">
<header class="header">
<button id="btn-back-anlagen" class="btn-icon">
<svg viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
</button>
<h1 id="anlage-name">Anlage</h1>
<button id="btn-sync" class="btn-icon sync-btn">
<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>
</button>
</header>
<div id="editor-content" class="editor-content">
<!-- Felder und Hutschienen -->
</div>
<div class="fab-container">
<button id="btn-add-panel" class="fab">
<svg viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span>Feld</span>
</button>
</div>
</div>
<!-- Automat hinzufügen/bearbeiten Modal -->
<div id="modal-add-equipment" class="modal">
<div class="modal-content">
<!-- Schritt 1: Typ wählen -->
<div id="eq-step-type">
<div class="modal-header">
<h2 id="equipment-modal-title">Automat hinzufügen</h2>
<button class="modal-close">&times;</button>
</div>
<div class="modal-body">
<p class="step-label">Typ wählen:</p>
<div id="type-grid" class="type-grid">
<!-- Typen werden geladen -->
</div>
</div>
</div>
<!-- Schritt 2: Felder ausfüllen -->
<div id="eq-step-fields" class="hidden">
<div class="modal-header">
<button id="btn-eq-back" class="btn-icon btn-back-modal">
<svg viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
</button>
<h2 id="eq-fields-title">Werte</h2>
<button class="modal-close">&times;</button>
</div>
<div class="modal-body">
<div id="eq-dynamic-fields">
<!-- Dynamische Felder vom Server -->
</div>
<div class="form-group">
<label>Bezeichnung</label>
<input type="text" id="equipment-label" placeholder="Leer = automatisch (z.B. R1.3)">
</div>
</div>
<div class="modal-footer">
<button id="btn-delete-equipment" class="btn btn-danger hidden">Löschen</button>
<div class="modal-footer-right">
<button id="btn-cancel-equipment" class="btn btn-secondary">Abbrechen</button>
<button id="btn-save-equipment" class="btn btn-primary">Speichern</button>
</div>
</div>
</div>
</div>
</div>
<!-- Bestätigungsdialog -->
<div id="modal-confirm" class="modal">
<div class="modal-content modal-small">
<div class="modal-header">
<h2 id="confirm-title">Löschen?</h2>
<button class="modal-close">&times;</button>
</div>
<div class="modal-body">
<p id="confirm-message">Diesen Automaten wirklich löschen?</p>
</div>
<div class="modal-footer">
<button class="btn btn-secondary modal-close">Abbrechen</button>
<button id="btn-confirm-ok" class="btn btn-danger">Löschen</button>
</div>
</div>
</div>
<!-- Verbindung (Abgang/Anschlusspunkt) Modal -->
<div id="modal-connection" class="modal">
<div class="modal-content modal-small">
<div class="modal-header">
<h2 id="connection-modal-title">Verbindung bearbeiten</h2>
<button class="modal-close">&times;</button>
</div>
<div class="modal-body">
<div class="form-row">
<div class="form-group form-group-grow">
<label>Typ</label>
<select id="conn-type" class="form-select">
<!-- Wird dynamisch befüllt -->
</select>
</div>
<div class="form-group form-group-color">
<label>Farbe</label>
<input type="color" id="conn-color" class="form-color" value="#3498db">
</div>
</div>
<div class="form-group">
<label>Bezeichnung</label>
<input type="text" id="conn-label" class="form-input" placeholder="z.B. Küche Steckdosen">
</div>
<div id="conn-output-fields">
<div class="form-group">
<label>Abgangsseite</label>
<div id="conn-side-grid" class="side-grid">
<button type="button" class="side-btn selected" data-side="bottom">&#9660; Unten</button>
<button type="button" class="side-btn" data-side="top">&#9650; Oben</button>
</div>
</div>
<div class="form-group">
<label>Medium</label>
<input type="text" id="conn-medium-type" class="form-input" placeholder="z.B. NYM-J, CAT6">
</div>
<div class="form-group">
<label>Spezifikation</label>
<input type="text" id="conn-medium-spec" class="form-input" placeholder="z.B. 3x1,5mm²">
</div>
<div class="form-group">
<label>Länge</label>
<input type="text" id="conn-medium-length" class="form-input" placeholder="z.B. 5m">
</div>
</div>
</div>
<div class="modal-footer">
<button id="btn-delete-connection" class="btn btn-danger hidden">Löschen</button>
<div class="modal-footer-right">
<button class="btn btn-secondary modal-close">Abbrechen</button>
<button id="btn-save-connection" class="btn btn-primary">Speichern</button>
</div>
</div>
</div>
</div>
<!-- Hutschiene hinzufügen/bearbeiten Modal -->
<div id="modal-add-carrier" class="modal">
<div class="modal-content modal-small">
<div class="modal-header">
<h2 id="carrier-modal-title">Hutschiene</h2>
<button class="modal-close">&times;</button>
</div>
<div class="modal-body">
<p class="step-label">Größe wählen:</p>
<div class="te-grid">
<button class="te-btn" data-te="12">12 TE</button>
<button class="te-btn" data-te="18">18 TE</button>
<button class="te-btn" data-te="24">24 TE</button>
<button class="te-btn" data-te="36">36 TE</button>
</div>
<div class="form-group" style="margin-top:16px;">
<label>Bezeichnung (optional)</label>
<input type="text" id="carrier-label" placeholder="z.B. Hutschiene 1">
</div>
</div>
<div class="modal-footer">
<button id="btn-delete-carrier" class="btn btn-danger hidden">Löschen</button>
<div class="modal-footer-right">
<button id="btn-save-carrier" class="btn btn-primary">Hinzufügen</button>
</div>
</div>
</div>
</div>
<!-- Feld hinzufügen Modal -->
<div id="modal-add-panel" class="modal">
<div class="modal-content modal-small">
<div class="modal-header">
<h2>Neues Feld</h2>
<button class="modal-close">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>Bezeichnung</label>
<input type="text" id="panel-label" placeholder="z.B. Feld 1">
</div>
</div>
<div class="modal-footer">
<div class="modal-footer-right">
<button class="btn btn-secondary modal-close">Abbrechen</button>
<button id="btn-save-panel" class="btn btn-primary">Hinzufügen</button>
</div>
</div>
</div>
</div>
<!-- Toast Notifications -->
<div id="toast" class="toast"></div>
</div>
<script src="<?php echo DOL_URL_ROOT; ?>/includes/jquery/js/jquery.min.js"></script>
<script>
// Dolibarr URL für AJAX
window.DOLIBARR_URL = '<?php echo DOL_URL_ROOT; ?>';
window.MODULE_URL = '<?php echo DOL_URL_ROOT; ?>/custom/kundenkarte';
</script>
<script src="js/pwa.js?v=2.8"></script>
</body>
</html>