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%;
}
.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 {
display: flex;
@@ -518,10 +539,9 @@
<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="toggleScannerBtn" class="button" type="button">Scanner starten</button>
<label style="display:flex; align-items:center; gap:8px; margin-left:6px;">
<input type="checkbox" id="keyboardScannerToggle">
<span style="font-size:0.9em;">Physischer Scanner</span>
</label>
<button id="physicalScannerBtn" class="button" type="button" style="margin-left:6px;">
Physischer Scanner starten
</button>
<button id="manualActionBtn" class="button" type="button" style="margin-left:6px; background:#4f46e5; color:white;">Code verarbeiten</button>
</div>
<div class="library-scan-reader-wrap" id="scanReaderWrap" style="display: none; margin-top: 15px;">
@@ -612,6 +632,16 @@
<div id="detailContent"></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>
@@ -842,6 +872,7 @@
let keyboardScannerEnabled = false;
let keyboardScanBuffer = '';
let keyboardLastKeyAt = 0;
let physicalScannerModalOpen = false;
const KEYBOARD_SCAN_INTERCHAR_MS = 100; // max time between keystrokes to consider them one scan
const physicalScanQueue = [];
let keyboardScanProcessing = false;
@@ -1186,9 +1217,62 @@
// =========================================================================
// 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) {
// Only active when explicitly enabled
if (!keyboardScannerEnabled) return;
if (!keyboardScannerEnabled || !physicalScannerModalOpen) return;
if (e.key === 'Escape') {
e.preventDefault();
closePhysicalScannerModal();
return;
}
const now = Date.now();
@@ -1201,6 +1285,9 @@
if (!code) return;
// 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);
return;
}
@@ -1213,6 +1300,8 @@
}
keyboardScanBuffer += e.key;
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.
e.preventDefault();
}
@@ -1226,6 +1315,9 @@
try {
while (physicalScanQueue.length > 0) {
await handleScanSuccess(physicalScanQueue.shift());
if (physicalScannerModalOpen) {
updatePhysicalScannerModal('Aktion abgeschlossen. Bereit für den nächsten Scan.', 'ok');
}
}
} finally {
keyboardScanProcessing = false;
@@ -1664,7 +1756,7 @@
const toggleBtn = document.getElementById('toggleScannerBtn');
const resetBtn = document.getElementById('resetCardBtn');
const modeSelect = document.getElementById('scanModeSelect');
const keyboardToggle = document.getElementById('keyboardScannerToggle');
const physicalScannerBtn = document.getElementById('physicalScannerBtn');
if (toggleBtn) {
toggleBtn.addEventListener('click', async () => {
@@ -1685,6 +1777,9 @@
if (modeSelect) {
modeSelect.addEventListener('change', () => {
if (physicalScannerModalOpen) {
updatePhysicalScannerModal('Warte auf Scan...', 'warn');
}
if (modeSelect.value === 'quick_toggle' && !activeStudentCardId) {
setScanStatus('Schnellmodus: zuerst Bibliotheksausweis scannen.', 'warn');
} else if (modeSelect.value === 'card_only') {
@@ -1693,18 +1788,24 @@
});
}
if (keyboardToggle) {
keyboardToggle.addEventListener('change', () => {
keyboardScannerEnabled = !!keyboardToggle.checked;
if (keyboardScannerEnabled) {
document.addEventListener('keydown', keyboardScanKeydownHandler);
setScanStatus('Physischer Scanner aktiv (Schnellmodus empfohlen).', 'ok');
} else {
document.removeEventListener('keydown', keyboardScanKeydownHandler);
setScanStatus('Physischer Scanner deaktiviert.', 'warn');
if (physicalScannerBtn) {
physicalScannerBtn.addEventListener('click', openPhysicalScannerModal);
}
const closePhysicalScannerBtn = document.getElementById('closePhysicalScannerBtn');
const stopPhysicalScannerBtn = document.getElementById('stopPhysicalScannerBtn');
if (closePhysicalScannerBtn) {
closePhysicalScannerBtn.addEventListener('click', closePhysicalScannerModal);
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