- Service Worker Version auf v1.1 erhöht (erzwingt Cache-Invalidierung) - pwa.php: Cache-Control no-store Header damit Browser immer die aktuelle HTML-Seite mit neuen JS/CSS-Versionen lädt Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
179 lines
6.4 KiB
PHP
179 lines
6.4 KiB
PHP
<?php
|
|
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
|
*
|
|
* Stundenzettel PWA - Standalone Mobile App
|
|
*/
|
|
|
|
// 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');
|
|
}
|
|
|
|
// Dolibarr-Umgebung laden (fuer Theme-Color und Config)
|
|
$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 && file_exists("../../../main.inc.php")) $res = @include "../../../main.inc.php";
|
|
if (!$res) die("Dolibarr konnte nicht geladen werden");
|
|
|
|
// Theme-Farbe aus Dolibarr
|
|
$themeColor = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#4390dc');
|
|
|
|
// Kein Caching fuer PWA-Einstiegsseite (damit neue JS/CSS-Versionen sofort geladen werden)
|
|
header('Cache-Control: no-cache, no-store, must-revalidate');
|
|
header('Pragma: no-cache');
|
|
header('Expires: 0');
|
|
|
|
?><!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="#1d1e20">
|
|
<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="STZ">
|
|
<title>Stundenzettel</title>
|
|
<link rel="manifest" href="manifest.json">
|
|
<link rel="icon" type="image/png" sizes="192x192" href="img/icon-192.png">
|
|
<link rel="apple-touch-icon" href="img/icon-192.png">
|
|
<link rel="stylesheet" href="css/pwa.css?v=2.9">
|
|
<style>:root { --primary: <?php echo htmlspecialchars($themeColor); ?>; }</style>
|
|
</head>
|
|
<body>
|
|
<div id="app" class="app">
|
|
|
|
<!-- Toast-Container -->
|
|
<div id="toast-container" class="toast-container"></div>
|
|
|
|
<!-- Loading-Overlay -->
|
|
<div id="loading-overlay" class="loading-overlay">
|
|
<div class="loading-spinner"></div>
|
|
</div>
|
|
|
|
<!-- Confirm-Dialog -->
|
|
<div id="confirm-dialog" class="confirm-dialog">
|
|
<div class="confirm-box">
|
|
<div id="confirm-title" class="confirm-title"></div>
|
|
<div id="confirm-text" class="confirm-text"></div>
|
|
<div class="confirm-actions">
|
|
<button id="confirm-cancel" class="btn btn-ghost">Abbrechen</button>
|
|
<button id="confirm-ok" class="btn btn-danger">OK</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bottom-Sheet (wiederverwendbar) -->
|
|
<div id="bottom-sheet-overlay" class="bottom-sheet-overlay"></div>
|
|
<div id="bottom-sheet" class="bottom-sheet">
|
|
<div class="bottom-sheet-handle"></div>
|
|
<div id="bottom-sheet-header" class="bottom-sheet-header"></div>
|
|
<div id="bottom-sheet-body" class="bottom-sheet-body"></div>
|
|
<div id="bottom-sheet-footer" class="bottom-sheet-footer"></div>
|
|
</div>
|
|
|
|
<!-- ===== LOGIN SCREEN ===== -->
|
|
<div id="screen-login" class="screen login-screen active">
|
|
<div class="login-container">
|
|
<div class="login-logo">
|
|
<svg viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z"/>
|
|
</svg>
|
|
</div>
|
|
<h1 class="login-title">Stundenzettel</h1>
|
|
<p class="login-subtitle">Mobile Zeiterfassung</p>
|
|
|
|
<form id="login-form" class="login-form">
|
|
<div class="form-group">
|
|
<label for="login-user">Benutzername</label>
|
|
<input type="text" id="login-user" autocomplete="username" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="login-pass">Passwort</label>
|
|
<input type="password" id="login-pass" autocomplete="current-password" required>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">Anmelden</button>
|
|
<p id="login-error" class="error-text"></p>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ===== SEARCH SCREEN ===== -->
|
|
<div id="screen-search" class="screen search-screen">
|
|
<div class="search-header">
|
|
<div class="search-input-wrap">
|
|
<span class="search-icon">🔍</span>
|
|
<input type="search" id="search-input" placeholder="Kunde suchen..." autocomplete="off">
|
|
</div>
|
|
<button id="btn-logout" class="btn btn-ghost btn-small">Abmelden</button>
|
|
</div>
|
|
<div id="search-results" class="search-results">
|
|
<div class="search-empty">
|
|
<p>Kundenname eingeben um Aufträge zu finden</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ===== MAIN SCREEN (Swipe-Panels) ===== -->
|
|
<div id="screen-main" class="screen main-screen">
|
|
<!-- Tab-Bar -->
|
|
<div class="tab-bar">
|
|
<div id="btn-back" class="back-btn">←</div>
|
|
<div class="tab-items">
|
|
<div class="tab-item" data-panel="0">Alle STZ</div>
|
|
<div class="tab-item" data-panel="1">Stundenzettel</div>
|
|
<div class="tab-item active" data-panel="2">Produktliste</div>
|
|
<div class="tab-item" data-panel="3">Lieferauflistung</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Swipe-Viewport -->
|
|
<div class="swipe-viewport">
|
|
<div id="swipe-container" class="swipe-container">
|
|
<!-- Panel 0: Alle Stundenzettel -->
|
|
<div id="panel-stzlist" class="swipe-panel"></div>
|
|
|
|
<!-- Panel 1: Stundenzettel (Leistungen + Merkzettel) -->
|
|
<div id="panel-stundenzettel" class="swipe-panel"></div>
|
|
|
|
<!-- Panel 2: Produktliste (Standard) -->
|
|
<div id="panel-products" class="swipe-panel"></div>
|
|
|
|
<!-- Panel 3: Lieferauflistung -->
|
|
<div id="panel-tracking" class="swipe-panel"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- FAB -->
|
|
<button id="fab-add" class="fab hidden">+</button>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- JavaScript -->
|
|
<script src="<?php echo DOL_URL_ROOT; ?>/includes/jquery/js/jquery.min.js"></script>
|
|
<script>
|
|
window.STZ_CONFIG = {
|
|
moduleUrl: '<?php echo dol_buildpath('/stundenzettel/', 1); ?>',
|
|
apiUrl: '<?php echo dol_buildpath('/stundenzettel/ajax/pwa_api.php', 1); ?>',
|
|
authUrl: '<?php echo dol_buildpath('/stundenzettel/ajax/pwa_auth.php', 1); ?>'
|
|
};
|
|
</script>
|
|
<script src="js/pwa.js?v=2.9"></script>
|
|
|
|
<!-- Service Worker Registration -->
|
|
<script>
|
|
if ('serviceWorker' in navigator) {
|
|
navigator.serviceWorker.register('sw.js')
|
|
.then(function(reg) { console.log('[STZ-PWA] Service Worker registriert'); })
|
|
.catch(function(err) { console.error('[STZ-PWA] SW Registrierung fehlgeschlagen:', err); });
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|