|
|
|
@@ -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">×</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" tabindex="0" aria-label="Eingehender Scan" 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,7 +872,10 @@
|
|
|
|
|
let keyboardScannerEnabled = false;
|
|
|
|
|
let keyboardScanBuffer = '';
|
|
|
|
|
let keyboardLastKeyAt = 0;
|
|
|
|
|
const KEYBOARD_SCAN_INTERCHAR_MS = 100; // max time between keystrokes to consider them one scan
|
|
|
|
|
let physicalScannerModalOpen = false;
|
|
|
|
|
const KEYBOARD_SCAN_INTERCHAR_MS = 500; // max time between slower scanner keystrokes
|
|
|
|
|
const physicalScanQueue = [];
|
|
|
|
|
let keyboardScanProcessing = false;
|
|
|
|
|
let editLibraryState = {
|
|
|
|
|
itemId: '',
|
|
|
|
|
seriesGroupId: '',
|
|
|
|
@@ -1184,29 +1217,86 @@
|
|
|
|
|
// =========================================================================
|
|
|
|
|
// 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 'Hinweis: Benötigt wird ein Mediencode zur Rückgabe.';
|
|
|
|
|
if (mode === 'card_only') return 'Hinweis: Benötigt wird ein Bibliotheksausweis.';
|
|
|
|
|
if (mode === 'quick_toggle' && !activeStudentCardId) {
|
|
|
|
|
return 'Hinweis: Zuerst wird ein Bibliotheksausweis benötigt.';
|
|
|
|
|
}
|
|
|
|
|
if (mode === 'quick_toggle') return 'Hinweis: Jetzt wird ein Mediencode benötigt.';
|
|
|
|
|
if (mode === 'continuous' && !activeStudentCardId) {
|
|
|
|
|
return 'Hinweis: Zuerst wird ein Bibliotheksausweis benötigt.';
|
|
|
|
|
}
|
|
|
|
|
return 'Hinweis: Jetzt wird der nächste Mediencode benötigt.';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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('physicalScannerCode')?.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;
|
|
|
|
|
|
|
|
|
|
// Ignore if focus is in an input/textarea/contenteditable to avoid interfering with typing
|
|
|
|
|
const active = document.activeElement;
|
|
|
|
|
if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.isContentEditable)) return;
|
|
|
|
|
if (e.key === 'Escape') {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
closePhysicalScannerModal();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
|
|
|
|
|
// If Enter/Return pressed -> finalize buffer
|
|
|
|
|
if (e.key === 'Enter') {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
const code = keyboardScanBuffer.trim();
|
|
|
|
|
keyboardScanBuffer = '';
|
|
|
|
|
keyboardLastKeyAt = 0;
|
|
|
|
|
if (!code) return;
|
|
|
|
|
|
|
|
|
|
// Process exactly like a camera scan - centralized logic handles the rest!
|
|
|
|
|
handleScanSuccess(code);
|
|
|
|
|
// Scanner keystrokes must not be submitted into the focused form field.
|
|
|
|
|
const displayCode = document.getElementById('physicalScannerCode');
|
|
|
|
|
if (displayCode) displayCode.textContent = code;
|
|
|
|
|
updatePhysicalScannerModal('Enter erkannt. Code wird verarbeitet...', 'warn');
|
|
|
|
|
processPhysicalScan(code);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only accept common printable characters; ignore modifier keys
|
|
|
|
|
// Accept all printable scanner characters, including letters and punctuation.
|
|
|
|
|
if (e.key.length === 1) {
|
|
|
|
|
// If time gap too big, start new buffer
|
|
|
|
|
if (keyboardLastKeyAt && (now - keyboardLastKeyAt) > KEYBOARD_SCAN_INTERCHAR_MS) {
|
|
|
|
@@ -1214,12 +1304,32 @@
|
|
|
|
|
}
|
|
|
|
|
keyboardScanBuffer += e.key;
|
|
|
|
|
keyboardLastKeyAt = now;
|
|
|
|
|
// Prevent default so scanner input doesn't accidentally move focus or trigger shortcuts
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleScanSuccess(decodedText) {
|
|
|
|
|
async function processPhysicalScan(code) {
|
|
|
|
|
physicalScanQueue.push(code);
|
|
|
|
|
if (keyboardScanProcessing) return;
|
|
|
|
|
|
|
|
|
|
keyboardScanProcessing = true;
|
|
|
|
|
try {
|
|
|
|
|
while (physicalScanQueue.length > 0) {
|
|
|
|
|
await handleScanSuccess(physicalScanQueue.shift());
|
|
|
|
|
if (physicalScannerModalOpen) {
|
|
|
|
|
const displayCode = document.getElementById('physicalScannerCode');
|
|
|
|
|
if (displayCode) displayCode.textContent = '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
keyboardScanProcessing = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function handleScanSuccess(decodedText) {
|
|
|
|
|
const scannedCode = typeof normalizeScannedCode === "function" ? normalizeScannedCode(decodedText) : decodedText;
|
|
|
|
|
if (!scannedCode) return;
|
|
|
|
|
|
|
|
|
@@ -1235,26 +1345,28 @@
|
|
|
|
|
|
|
|
|
|
// 1. Modus: Nur Rückgabe
|
|
|
|
|
if (mode === 'return_only') {
|
|
|
|
|
returnByCode(scannedCode);
|
|
|
|
|
await returnByCode(scannedCode);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. Modus: Nur Ausweis
|
|
|
|
|
if (mode === 'card_only') {
|
|
|
|
|
setActiveStudentCard(scannedCode);
|
|
|
|
|
setScanStatus(`Ausweis gesetzt: ${activeStudentCardId}`, 'ok');
|
|
|
|
|
setScanStatus(`Ausweis gesetzt: ${activeStudentCardId}. Bitte den nächsten Ausweis scannen.`, 'ok');
|
|
|
|
|
showSmallConfirm(`Ausweis erkannt: ${activeStudentCardId}. Sie können den nächsten scannen.`, 'ok');
|
|
|
|
|
updatePhysicalScannerModal(`Ausweis erkannt. Bereit für den nächsten Scan.`, 'ok');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. Modus: Schnellmodus (1x Ausweis, 1x Buch)
|
|
|
|
|
if (mode === 'quick_toggle') {
|
|
|
|
|
processQuickToggleScan(scannedCode);
|
|
|
|
|
await processQuickToggleScan(scannedCode);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. Modus: Dauermodus (1x Ausweis, Nx Bücher)
|
|
|
|
|
if (mode === 'continuous') {
|
|
|
|
|
processContinuousScan(scannedCode);
|
|
|
|
|
await processContinuousScan(scannedCode);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -1268,9 +1380,10 @@
|
|
|
|
|
setActiveStudentCard(scannedCode);
|
|
|
|
|
setScanStatus(`Neuer Ausweis gesetzt: ${activeStudentCardId}. Bitte Medien scannen.`, 'ok');
|
|
|
|
|
showSmallConfirm(`Benutzer gewechselt zu: ${activeStudentCardId}`, 'ok');
|
|
|
|
|
updatePhysicalScannerModal('Ausweis erkannt. Jetzt Mediencodes scannen.', 'ok');
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
if (document.getElementById('scanModeSelect').value === 'continuous') {
|
|
|
|
|
if (!physicalScannerModalOpen && document.getElementById('scanModeSelect').value === 'continuous') {
|
|
|
|
|
startScanner();
|
|
|
|
|
}
|
|
|
|
|
}, 1000);
|
|
|
|
@@ -1280,9 +1393,10 @@
|
|
|
|
|
if (!activeStudentCardId) {
|
|
|
|
|
setScanStatus('Kein Ausweis aktiv! Bitte zuerst einen Schülerausweis scannen.', 'error');
|
|
|
|
|
showSmallConfirm('Bitte zuerst Ausweis scannen', 'error');
|
|
|
|
|
updatePhysicalScannerModal('Kein Ausweis aktiv. Bitte zuerst den Ausweis scannen.', 'error');
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
if (document.getElementById('scanModeSelect').value === 'continuous') {
|
|
|
|
|
if (!physicalScannerModalOpen && document.getElementById('scanModeSelect').value === 'continuous') {
|
|
|
|
|
startScanner();
|
|
|
|
|
}
|
|
|
|
|
}, 2000);
|
|
|
|
@@ -1308,15 +1422,19 @@
|
|
|
|
|
if (!response.ok || !result.ok) {
|
|
|
|
|
setScanStatus(result.message || 'Scan-Aktion fehlgeschlagen.', 'error');
|
|
|
|
|
showSmallConfirm(result.message || 'Aktion fehlgeschlagen.', 'error');
|
|
|
|
|
updatePhysicalScannerModal(result.message || 'Scan-Aktion fehlgeschlagen.', 'error');
|
|
|
|
|
} else if (result.action === 'borrowed') {
|
|
|
|
|
setScanStatus(`Ausgeliehen: ${result.item_name}`, 'ok');
|
|
|
|
|
showSmallConfirm(`Ausgeliehen: ${result.item_name}`, 'ok');
|
|
|
|
|
updatePhysicalScannerModal('Ausleihe erfolgreich. Bereit für den nächsten Mediencode.', 'ok');
|
|
|
|
|
} else if (result.action === 'returned') {
|
|
|
|
|
setScanStatus(`Zurückgegeben: ${result.item_name}`, 'ok');
|
|
|
|
|
showSmallConfirm(`Zurückgegeben: ${result.item_name}`, 'ok');
|
|
|
|
|
updatePhysicalScannerModal('Rückgabe erfolgreich. Bereit für den nächsten Mediencode.', 'ok');
|
|
|
|
|
} else {
|
|
|
|
|
setScanStatus(result.message || 'Aktion durchgeführt.', 'ok');
|
|
|
|
|
showSmallConfirm(result.message || 'Erfolgreich', 'ok');
|
|
|
|
|
updatePhysicalScannerModal('Aktion erfolgreich. Bereit für den nächsten Mediencode.', 'ok');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tabellen-Ansicht aktualisieren
|
|
|
|
@@ -1327,10 +1445,11 @@
|
|
|
|
|
console.error('Continuous scan action failed:', err);
|
|
|
|
|
setScanStatus('Fehler beim Verarbeiten des Scans.', 'error');
|
|
|
|
|
showSmallConfirm('Fehler im Netzwerk/System', 'error');
|
|
|
|
|
updatePhysicalScannerModal('Fehler beim Verarbeiten. Bitte erneut scannen.', 'error');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
if (document.getElementById('scanModeSelect').value === 'continuous') {
|
|
|
|
|
if (!physicalScannerModalOpen && document.getElementById('scanModeSelect').value === 'continuous') {
|
|
|
|
|
startScanner();
|
|
|
|
|
}
|
|
|
|
|
}, 1500);
|
|
|
|
@@ -1341,7 +1460,9 @@
|
|
|
|
|
// 2. Wenn kein Ausweis gesetzt ist, wird der Code als Ausweis interpretiert
|
|
|
|
|
if (!activeStudentCardId) {
|
|
|
|
|
setActiveStudentCard(scannedCode);
|
|
|
|
|
setScanStatus(`Ausweis gesetzt: ${activeStudentCardId}`, 'ok');
|
|
|
|
|
setScanStatus(`Ausweis gesetzt: ${activeStudentCardId}. Bitte den nächsten Mediencode scannen.`, 'ok');
|
|
|
|
|
showSmallConfirm(`Ausweis erkannt: ${activeStudentCardId}. Bitte jetzt den nächsten Mediencode scannen.`, 'ok');
|
|
|
|
|
updatePhysicalScannerModal('Ausweis erkannt. Jetzt den Mediencode scannen.', 'ok');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1350,7 +1471,11 @@
|
|
|
|
|
setScanStatus('Verarbeite Mediencode...', 'warn');
|
|
|
|
|
const response = await fetch('/api/library_scan_action', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
'X-CSRFToken': '{{ csrf_token }}',
|
|
|
|
|
'X-CSRF-Token': '{{ csrf_token }}'
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
student_card_id: activeStudentCardId,
|
|
|
|
|
item_code: scannedCode
|
|
|
|
@@ -1360,24 +1485,29 @@
|
|
|
|
|
const result = await response.json();
|
|
|
|
|
if (!response.ok || !result.ok) {
|
|
|
|
|
setScanStatus(result.message || 'Scan-Aktion fehlgeschlagen.', 'error');
|
|
|
|
|
updatePhysicalScannerModal(result.message || 'Scan-Aktion fehlgeschlagen.', 'error');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.action === 'borrowed') {
|
|
|
|
|
setScanStatus(`Ausgeliehen: ${result.item_name}`, 'ok');
|
|
|
|
|
showSmallConfirm(`Ausgeliehen: ${result.item_name}`, 'ok');
|
|
|
|
|
updatePhysicalScannerModal('Ausleihe erfolgreich. Bereit für den nächsten Mediencode.', 'ok');
|
|
|
|
|
} else if (result.action === 'returned') {
|
|
|
|
|
setScanStatus(`Zurückgegeben: ${result.item_name}`, 'ok');
|
|
|
|
|
showSmallConfirm(`Zurückgegeben: ${result.item_name}`, 'ok');
|
|
|
|
|
updatePhysicalScannerModal('Rückgabe erfolgreich. Bereit für den nächsten Mediencode.', 'ok');
|
|
|
|
|
} else {
|
|
|
|
|
setScanStatus(result.message || 'Aktion durchgeführt.', 'ok');
|
|
|
|
|
showSmallConfirm(result.message || 'Aktion durchgeführt.', 'ok');
|
|
|
|
|
updatePhysicalScannerModal('Aktion erfolgreich. Bereit für den nächsten Mediencode.', 'ok');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await loadLibraryItems();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Quick scan action failed:', err);
|
|
|
|
|
setScanStatus('Fehler beim Verarbeiten des Scans.', 'error');
|
|
|
|
|
updatePhysicalScannerModal('Fehler beim Verarbeiten. Bitte erneut scannen.', 'error');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1387,23 +1517,30 @@
|
|
|
|
|
try {
|
|
|
|
|
const resp = await fetch('/api/library_return_by_code', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
'X-CSRFToken': '{{ csrf_token }}',
|
|
|
|
|
'X-CSRF-Token': '{{ csrf_token }}'
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({ item_code: code })
|
|
|
|
|
});
|
|
|
|
|
const result = await resp.json();
|
|
|
|
|
if (!resp.ok || !result.ok) {
|
|
|
|
|
setScanStatus(result.message || 'Rückgabe fehlgeschlagen.', 'error');
|
|
|
|
|
showSmallConfirm(result.message || 'Rückgabe fehlgeschlagen.', 'error');
|
|
|
|
|
updatePhysicalScannerModal(result.message || 'Rückgabe fehlgeschlagen.', 'error');
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
setScanStatus(result.message || `Zurückgegeben: ${result.item_name || ''}`, 'ok');
|
|
|
|
|
showSmallConfirm(result.message || `Zurückgegeben: ${result.item_name || ''}`, 'ok');
|
|
|
|
|
updatePhysicalScannerModal('Rückgabe erfolgreich. Bereit für den nächsten Mediencode.', 'ok');
|
|
|
|
|
await loadLibraryItems();
|
|
|
|
|
return true;
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Return by code failed:', err);
|
|
|
|
|
setScanStatus('Fehler bei Rückgabe.', 'error');
|
|
|
|
|
showSmallConfirm('Fehler bei Rückgabe.', 'error');
|
|
|
|
|
updatePhysicalScannerModal('Fehler bei Rückgabe. Bitte erneut scannen.', 'error');
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -1649,7 +1786,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 () => {
|
|
|
|
@@ -1670,6 +1807,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') {
|
|
|
|
@@ -1678,18 +1818,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
|
|
|
|
|