kundenkarte/pwa.php
data 6e88f0eb87 feat(pwa): Kontakt-Adressen, Grid-Layout, Abgang-Labels, jQuery
- Kontakt-Adressen als aufklappbare Gruppen in Anlagen-Übersicht
- Equipment-Blöcke als CSS Grid (TE-basiert) statt Flex-Wrap
- Abgang-Labels (Outputs) über/unter Automaten, Toggle-Button
- jQuery statt eigener ElementCollection, aus Dolibarr geladen
- Design-System auf Dolibarr Dark Theme Variablen umgestellt
- Session-State-Wiederherstellung bei Refresh
- Browser-History Support (Hardware-Zurück)
- Quick-Select erweitert: AFDD, FI/LS-Kombi
- Intelligente Positionsberechnung mit Lücken-Erkennung
- Hutschiene zeigt belegt/gesamt TE
- $user->getrights() nach Token-Validierung
- Doku aktualisiert

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:37:17 +01:00

240 lines
8.2 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">
<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-toggle-labels" class="btn-toggle-labels labels-top" title="Abgänge oben/unten">
<svg viewBox="0 0 24 24"><path d="M7 14l5-5 5 5z"/></svg>
</button>
<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 Modal -->
<div id="modal-add-equipment" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>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>
<!-- Werte + Abgang (erscheint nach Typ-Auswahl) -->
<div id="step-values" class="step-values-area">
<div id="value-fields" class="value-fields">
<!-- Dynamische Quick-Select Chips -->
</div>
<div class="form-group">
<label>Abgang / Beschriftung</label>
<input type="text" id="equipment-label" placeholder="z.B. Küche Licht, Steckdose Bad">
</div>
</div>
</div>
<div class="modal-footer">
<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>
<!-- Hutschiene hinzufügen Modal -->
<div id="modal-add-carrier" class="modal">
<div class="modal-content modal-small">
<div class="modal-header">
<h2>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 class="btn btn-secondary modal-close">Abbrechen</button>
<button id="btn-save-carrier" class="btn btn-primary">Hinzufügen</button>
</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">
<button class="btn btn-secondary modal-close">Abbrechen</button>
<button id="btn-save-panel" class="btn btn-primary">Hinzufügen</button>
</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"></script>
</body>
</html>