changes to the scanning functrionality
Release Inventarsystem / release-docker (push) Successful in 2m22s

This commit is contained in:
2026-09-08 18:36:34 +02:00
parent 0114fec928
commit 0e85337fc5
+116 -15
View File
@@ -451,6 +451,27 @@
height: 100%; height: 100%;
} }
.physical-scanner-modal {
z-index: 1100;
}
.physical-scanner-modal .modal-content {
max-width: 520px;
text-align: center;
}
.physical-scanner-code {
min-height: 28px;
margin: 18px 0 8px;
padding: 12px;
border: 1px dashed #cbd5e1;
border-radius: 6px;
color: var(--ui-text);
font-family: monospace;
font-size: 1.2em;
letter-spacing: 2px;
}
.detail-gallery-container { .detail-gallery-container {
display: flex; display: flex;
@@ -518,10 +539,9 @@
<input type="text" id="manualItemCode" placeholder="Manueller Mediencode (optional)" style="min-width:180px;"> <input type="text" id="manualItemCode" placeholder="Manueller Mediencode (optional)" style="min-width:180px;">
<button id="resetCardBtn" class="button" type="button">Feld zurücksetzen</button> <button id="resetCardBtn" class="button" type="button">Feld zurücksetzen</button>
<button id="toggleScannerBtn" class="button" type="button">Scanner starten</button> <button id="toggleScannerBtn" class="button" type="button">Scanner starten</button>
<label style="display:flex; align-items:center; gap:8px; margin-left:6px;"> <button id="physicalScannerBtn" class="button" type="button" style="margin-left:6px;">
<input type="checkbox" id="keyboardScannerToggle"> Physischer Scanner starten
<span style="font-size:0.9em;">Physischer Scanner</span> </button>
</label>
<button id="manualActionBtn" class="button" type="button" style="margin-left:6px; background:#4f46e5; color:white;">Code verarbeiten</button> <button id="manualActionBtn" class="button" type="button" style="margin-left:6px; background:#4f46e5; color:white;">Code verarbeiten</button>
</div> </div>
<div class="library-scan-reader-wrap" id="scanReaderWrap" style="display: none; margin-top: 15px;"> <div class="library-scan-reader-wrap" id="scanReaderWrap" style="display: none; margin-top: 15px;">
@@ -612,6 +632,16 @@
<div id="detailContent"></div> <div id="detailContent"></div>
</div> </div>
</div> </div>
<div id="physicalScannerModal" class="modal physical-scanner-modal" style="display: none;" role="dialog" aria-modal="true" aria-labelledby="physicalScannerTitle">
<div class="modal-content">
<span class="close" id="closePhysicalScannerBtn" role="button" tabindex="0" aria-label="Physischen Scanner schließen">&times;</span>
<h2 id="physicalScannerTitle">Physischer Scanner</h2>
<p id="physicalScannerInstruction">Scanner ist bereit. Bitte den angezeigten Code scannen.</p>
<div id="physicalScannerCode" class="physical-scanner-code" aria-live="polite"></div>
<p id="physicalScannerStatus" class="library-scan-status" aria-live="polite">Warte auf Scan...</p>
<button id="stopPhysicalScannerBtn" class="button" type="button">Scanner schließen</button>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2/dist/quagga.js"></script> <script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2/dist/quagga.js"></script>
<script> <script>
@@ -842,6 +872,7 @@
let keyboardScannerEnabled = false; let keyboardScannerEnabled = false;
let keyboardScanBuffer = ''; let keyboardScanBuffer = '';
let keyboardLastKeyAt = 0; let keyboardLastKeyAt = 0;
let physicalScannerModalOpen = false;
const KEYBOARD_SCAN_INTERCHAR_MS = 100; // max time between keystrokes to consider them one scan const KEYBOARD_SCAN_INTERCHAR_MS = 100; // max time between keystrokes to consider them one scan
const physicalScanQueue = []; const physicalScanQueue = [];
let keyboardScanProcessing = false; let keyboardScanProcessing = false;
@@ -1186,9 +1217,62 @@
// ========================================================================= // =========================================================================
// Keyboard scanner handling (physical scanners that send chars then Enter) // Keyboard scanner handling (physical scanners that send chars then Enter)
// ========================================================================= // =========================================================================
function getPhysicalScannerInstruction() {
const mode = document.getElementById('scanModeSelect')?.value || 'card_only';
if (mode === 'return_only') return 'Bitte den Mediencode scannen.';
if (mode === 'card_only') return 'Bitte den Bibliotheksausweis scannen.';
if (mode === 'quick_toggle' && !activeStudentCardId) return 'Bitte zuerst den Bibliotheksausweis scannen.';
if (mode === 'quick_toggle') return 'Bitte den Mediencode scannen.';
if (mode === 'continuous' && !activeStudentCardId) return 'Bitte zuerst den Bibliotheksausweis scannen.';
return 'Bitte den nächsten Mediencode scannen.';
}
function updatePhysicalScannerModal(message, kind = '') {
const instruction = document.getElementById('physicalScannerInstruction');
const status = document.getElementById('physicalScannerStatus');
if (instruction) instruction.textContent = getPhysicalScannerInstruction();
if (status) {
status.textContent = message;
status.classList.remove('ok', 'warn', 'error');
if (kind) status.classList.add(kind);
}
}
function openPhysicalScannerModal() {
const modal = document.getElementById('physicalScannerModal');
if (!modal) return;
physicalScannerModalOpen = true;
keyboardScannerEnabled = true;
keyboardScanBuffer = '';
keyboardLastKeyAt = 0;
modal.style.display = 'flex';
updatePhysicalScannerModal('Warte auf Scan...', 'warn');
document.addEventListener('keydown', keyboardScanKeydownHandler);
document.getElementById('closePhysicalScannerBtn')?.focus();
}
function closePhysicalScannerModal() {
physicalScannerModalOpen = false;
keyboardScannerEnabled = false;
keyboardScanBuffer = '';
keyboardLastKeyAt = 0;
document.removeEventListener('keydown', keyboardScanKeydownHandler);
const modal = document.getElementById('physicalScannerModal');
if (modal) modal.style.display = 'none';
const code = document.getElementById('physicalScannerCode');
if (code) code.textContent = '';
}
function keyboardScanKeydownHandler(e) { function keyboardScanKeydownHandler(e) {
// Only active when explicitly enabled // Only active when explicitly enabled
if (!keyboardScannerEnabled) return; if (!keyboardScannerEnabled || !physicalScannerModalOpen) return;
if (e.key === 'Escape') {
e.preventDefault();
closePhysicalScannerModal();
return;
}
const now = Date.now(); const now = Date.now();
@@ -1201,6 +1285,9 @@
if (!code) return; if (!code) return;
// Scanner keystrokes must not be submitted into the focused form field. // Scanner keystrokes must not be submitted into the focused form field.
const displayCode = document.getElementById('physicalScannerCode');
if (displayCode) displayCode.textContent = code;
updatePhysicalScannerModal('Code empfangen. Verarbeitung läuft...', 'warn');
processPhysicalScan(code); processPhysicalScan(code);
return; return;
} }
@@ -1213,6 +1300,8 @@
} }
keyboardScanBuffer += e.key; keyboardScanBuffer += e.key;
keyboardLastKeyAt = now; keyboardLastKeyAt = now;
const displayCode = document.getElementById('physicalScannerCode');
if (displayCode) displayCode.textContent = keyboardScanBuffer;
// Prevent default so scanner input is captured even while an input has focus. // Prevent default so scanner input is captured even while an input has focus.
e.preventDefault(); e.preventDefault();
} }
@@ -1226,6 +1315,9 @@
try { try {
while (physicalScanQueue.length > 0) { while (physicalScanQueue.length > 0) {
await handleScanSuccess(physicalScanQueue.shift()); await handleScanSuccess(physicalScanQueue.shift());
if (physicalScannerModalOpen) {
updatePhysicalScannerModal('Aktion abgeschlossen. Bereit für den nächsten Scan.', 'ok');
}
} }
} finally { } finally {
keyboardScanProcessing = false; keyboardScanProcessing = false;
@@ -1664,7 +1756,7 @@
const toggleBtn = document.getElementById('toggleScannerBtn'); const toggleBtn = document.getElementById('toggleScannerBtn');
const resetBtn = document.getElementById('resetCardBtn'); const resetBtn = document.getElementById('resetCardBtn');
const modeSelect = document.getElementById('scanModeSelect'); const modeSelect = document.getElementById('scanModeSelect');
const keyboardToggle = document.getElementById('keyboardScannerToggle'); const physicalScannerBtn = document.getElementById('physicalScannerBtn');
if (toggleBtn) { if (toggleBtn) {
toggleBtn.addEventListener('click', async () => { toggleBtn.addEventListener('click', async () => {
@@ -1685,6 +1777,9 @@
if (modeSelect) { if (modeSelect) {
modeSelect.addEventListener('change', () => { modeSelect.addEventListener('change', () => {
if (physicalScannerModalOpen) {
updatePhysicalScannerModal('Warte auf Scan...', 'warn');
}
if (modeSelect.value === 'quick_toggle' && !activeStudentCardId) { if (modeSelect.value === 'quick_toggle' && !activeStudentCardId) {
setScanStatus('Schnellmodus: zuerst Bibliotheksausweis scannen.', 'warn'); setScanStatus('Schnellmodus: zuerst Bibliotheksausweis scannen.', 'warn');
} else if (modeSelect.value === 'card_only') { } else if (modeSelect.value === 'card_only') {
@@ -1693,18 +1788,24 @@
}); });
} }
if (keyboardToggle) { if (physicalScannerBtn) {
keyboardToggle.addEventListener('change', () => { physicalScannerBtn.addEventListener('click', openPhysicalScannerModal);
keyboardScannerEnabled = !!keyboardToggle.checked; }
if (keyboardScannerEnabled) {
document.addEventListener('keydown', keyboardScanKeydownHandler); const closePhysicalScannerBtn = document.getElementById('closePhysicalScannerBtn');
setScanStatus('Physischer Scanner aktiv (Schnellmodus empfohlen).', 'ok'); const stopPhysicalScannerBtn = document.getElementById('stopPhysicalScannerBtn');
} else { if (closePhysicalScannerBtn) {
document.removeEventListener('keydown', keyboardScanKeydownHandler); closePhysicalScannerBtn.addEventListener('click', closePhysicalScannerModal);
setScanStatus('Physischer Scanner deaktiviert.', 'warn'); closePhysicalScannerBtn.addEventListener('keydown', (event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
closePhysicalScannerModal();
} }
}); });
} }
if (stopPhysicalScannerBtn) {
stopPhysicalScannerBtn.addEventListener('click', closePhysicalScannerModal);
}
} }
// Run when DOM structure is entirely ready // Run when DOM structure is entirely ready