v6.8: Stopp-Button Fix, persistente Freitext-Lieferant-Speicherung, SW-Update
- Stopp-Button: overflow:hidden auf Video-Container, z-index auf Controls, pointer-events:none auf Quagga-Canvas damit der Button klickbar bleibt - Freitext-Lieferant wird persistent in localStorage gespeichert (hbs_config) und bei Dropdown-Aenderung sofort aktualisiert - Service Worker: Network-first fuer eigene Assets (JS/CSS), Cache-first nur noch fuer CDN-Libraries - Letzte Bestell-ID (lastOrderId) ebenfalls persistent - Migration alter localStorage-Keys in neue zentrale Config Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
08221a660d
commit
d81f215f59
4 changed files with 107 additions and 21 deletions
|
|
@ -76,7 +76,7 @@ class modHandyBarcodeScanner extends DolibarrModules
|
|||
$this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@handybarcodescanner'
|
||||
|
||||
// Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z'
|
||||
$this->version = '6.0';
|
||||
$this->version = '6.8';
|
||||
// Url to the file with your last numberversion of this module
|
||||
//$this->url_last_version = 'http://www.example.com/versionmodule.txt';
|
||||
|
||||
|
|
|
|||
|
|
@ -46,10 +46,40 @@
|
|||
let allSuppliers = [];
|
||||
let quaggaInitialized = false;
|
||||
let openDialogCount = 0; // Zaehlt offene Dialoge
|
||||
let lastFreetextSupplierId = null; // Zuletzt verwendeter Freitext-Lieferant
|
||||
|
||||
// Order overview state
|
||||
let lastOrderId = null;
|
||||
// Persistente Einstellungen - eine zentrale Config statt einzelner Keys
|
||||
const STORAGE_KEY = 'hbs_config';
|
||||
function loadConfig() {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
function saveConfig(key, value) {
|
||||
const cfg = loadConfig();
|
||||
cfg[key] = value;
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(cfg));
|
||||
}
|
||||
|
||||
// Migration: alte einzelne Keys -> neue zentrale Config
|
||||
(function migrateOldKeys() {
|
||||
const oldSupplier = localStorage.getItem('hbs_lastFreetextSupplierId');
|
||||
const oldOrder = localStorage.getItem('hbs_lastOrderId');
|
||||
if (oldSupplier) {
|
||||
saveConfig('lastFreetextSupplierId', oldSupplier);
|
||||
localStorage.removeItem('hbs_lastFreetextSupplierId');
|
||||
}
|
||||
if (oldOrder) {
|
||||
saveConfig('lastOrderId', parseInt(oldOrder));
|
||||
localStorage.removeItem('hbs_lastOrderId');
|
||||
}
|
||||
})();
|
||||
|
||||
let lastFreetextSupplierId = loadConfig().lastFreetextSupplierId || null;
|
||||
|
||||
// Order overview state (persistent über App-Neustarts)
|
||||
let lastOrderId = loadConfig().lastOrderId || null;
|
||||
let cachedOrders = [];
|
||||
let currentOrderDetail = null;
|
||||
|
||||
|
|
@ -277,10 +307,15 @@
|
|||
Quagga.start();
|
||||
quaggaInitialized = true;
|
||||
isScanning = true;
|
||||
elements.startBtn.disabled = false;
|
||||
elements.startBtn.classList.add('hidden');
|
||||
elements.stopBtn.classList.remove('hidden');
|
||||
elements.videoContainer.classList.add('scanning');
|
||||
|
||||
// Quagga-Canvas darf keine Klicks abfangen
|
||||
const canvases = elements.videoContainer.querySelectorAll('canvas');
|
||||
canvases.forEach(function(c) { c.style.pointerEvents = 'none'; });
|
||||
|
||||
// Register detection handler
|
||||
Quagga.onDetected(onBarcodeDetected);
|
||||
|
||||
|
|
@ -728,6 +763,7 @@
|
|||
showToast(`${CONFIG.lang.added}: ${productName} (${qty}x)`, 'success');
|
||||
// Save last order ID for auto-open in order view
|
||||
lastOrderId = data.order_id;
|
||||
saveConfig('lastOrderId', data.order_id);
|
||||
hideResult();
|
||||
} else {
|
||||
showToast(data.error || CONFIG.lang.error, 'error');
|
||||
|
|
@ -856,6 +892,8 @@
|
|||
|
||||
pauseScanner();
|
||||
|
||||
const savedSupplierId = loadConfig().lastFreetextSupplierId;
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'freetext-modal';
|
||||
modal.id = 'freetext-modal';
|
||||
|
|
@ -866,7 +904,7 @@
|
|||
<div>
|
||||
<label>Lieferant</label>
|
||||
<select id="freetext-supplier">
|
||||
${allSuppliers.map(s => `<option value="${s.id}" ${s.id == lastFreetextSupplierId ? 'selected' : ''}>${escapeHtml(s.name)}</option>`).join('')}
|
||||
${allSuppliers.map(s => `<option value="${s.id}">${escapeHtml(s.name)}</option>`).join('')}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -892,6 +930,17 @@
|
|||
`;
|
||||
document.body.appendChild(modal);
|
||||
|
||||
// Gespeicherten Lieferant per JS setzen
|
||||
const supplierSelect = document.getElementById('freetext-supplier');
|
||||
if (savedSupplierId) {
|
||||
supplierSelect.value = savedSupplierId;
|
||||
}
|
||||
|
||||
// Bei jeder Aenderung sofort speichern
|
||||
supplierSelect.addEventListener('change', function() {
|
||||
saveConfig('lastFreetextSupplierId', this.value);
|
||||
});
|
||||
|
||||
function closeModal() {
|
||||
modal.remove();
|
||||
resumeScanner();
|
||||
|
|
@ -923,8 +972,9 @@
|
|||
return;
|
||||
}
|
||||
|
||||
// Lieferant merken für nächstes Mal
|
||||
// Lieferant merken für nächstes Mal (auch über Neustarts hinweg)
|
||||
lastFreetextSupplierId = supplierId;
|
||||
saveConfig('lastFreetextSupplierId', supplierId);
|
||||
|
||||
showLoading();
|
||||
|
||||
|
|
@ -942,6 +992,7 @@
|
|||
if (data.success) {
|
||||
showToast(`Hinzugefügt: ${description} (${qty}x)`, 'success');
|
||||
lastOrderId = data.order_id;
|
||||
saveConfig('lastOrderId', data.order_id);
|
||||
if (closeModalCallback) closeModalCallback();
|
||||
} else {
|
||||
showToast(data.error || CONFIG.lang.error, 'error');
|
||||
|
|
|
|||
33
pwa.php
33
pwa.php
|
|
@ -53,7 +53,7 @@ $colormain = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#0077b3');
|
|||
<title>Barcode Scanner</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
<link rel="apple-touch-icon" href="img/icon-192.png">
|
||||
<link rel="stylesheet" href="css/scanner.css?v=60">
|
||||
<link rel="stylesheet" href="css/scanner.css?v=61">
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
|
|
@ -314,6 +314,7 @@ $colormain = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#0077b3');
|
|||
aspect-ratio: 4/3;
|
||||
max-height: 35vh;
|
||||
background: #000;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#scanner-video {
|
||||
|
|
@ -351,26 +352,44 @@ $colormain = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#0077b3');
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.scan-btn {
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
padding: 12px 28px;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
letter-spacing: 0.3px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.scan-btn:active {
|
||||
transform: scale(0.95);
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
.scan-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: wait;
|
||||
transform: none;
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.scan-btn.start {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(0, 119, 179, 0.4);
|
||||
}
|
||||
|
||||
.scan-btn.stop {
|
||||
background: var(--danger);
|
||||
background: #e53935;
|
||||
color: white;
|
||||
box-shadow: 0 2px 8px rgba(229, 57, 53, 0.4);
|
||||
}
|
||||
|
||||
/* Tool buttons next to scan button */
|
||||
|
|
@ -1280,6 +1299,6 @@ $colormain = getDolGlobalString('THEME_ELDY_TOPMENU_BACK1', '#0077b3');
|
|||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2@1.8.4/dist/quagga.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.6/dist/JsBarcode.all.min.js"></script>
|
||||
<script src="<?php echo dol_buildpath('/handybarcodescanner/js/scanner.js', 1); ?>?v=60"></script>
|
||||
<script src="<?php echo dol_buildpath('/handybarcodescanner/js/scanner.js', 1); ?>?v=61"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
20
sw.js
20
sw.js
|
|
@ -1,5 +1,5 @@
|
|||
// Service Worker for HandyBarcodeScanner PWA
|
||||
const CACHE_NAME = 'scanner-v6.0';
|
||||
const CACHE_NAME = 'scanner-v6.1';
|
||||
const ASSETS = [
|
||||
'pwa.php',
|
||||
'css/scanner.css',
|
||||
|
|
@ -60,7 +60,11 @@ self.addEventListener('fetch', event => {
|
|||
return;
|
||||
}
|
||||
|
||||
// Cache first for static assets
|
||||
// Eigene Assets: Network first, Cache als Fallback (damit Updates sofort ankommen)
|
||||
// CDN-Assets: Cache first (aendern sich nie dank versionierter URL)
|
||||
const isCDN = url.hostname !== location.hostname;
|
||||
|
||||
if (isCDN) {
|
||||
event.respondWith(
|
||||
caches.match(event.request).then(cached => {
|
||||
return cached || fetch(event.request).then(response => {
|
||||
|
|
@ -72,6 +76,18 @@ self.addEventListener('fetch', event => {
|
|||
});
|
||||
})
|
||||
);
|
||||
} else {
|
||||
// Network first fuer eigene JS/CSS/Bilder
|
||||
event.respondWith(
|
||||
fetch(event.request).then(response => {
|
||||
if (response.ok) {
|
||||
const clone = response.clone();
|
||||
caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone));
|
||||
}
|
||||
return response;
|
||||
}).catch(() => caches.match(event.request))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for messages from main app
|
||||
|
|
|
|||
Loading…
Reference in a new issue