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>
238 lines
7.8 KiB
PHP
238 lines
7.8 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="apple-touch-icon" href="img/pwa-icon-192.png">
|
|
<link rel="stylesheet" href="css/pwa.css">
|
|
</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 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">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<!-- Step 1: Typ wählen -->
|
|
<div id="step-type" class="step active">
|
|
<p class="step-label">Typ wählen:</p>
|
|
<div id="type-grid" class="type-grid">
|
|
<!-- Typen werden geladen -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 2: Werte eingeben -->
|
|
<div id="step-values" class="step">
|
|
<p class="step-label">Werte:</p>
|
|
<div id="value-fields" class="value-fields">
|
|
<!-- Dynamische Felder -->
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Abgang / Beschriftung</label>
|
|
<input type="text" id="equipment-label" placeholder="z.B. Küche Licht">
|
|
</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">×</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">×</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>
|
|
// 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>
|