changes to the scanning functrionality
Release Inventarsystem / release-docker (push) Successful in 2m22s
Release Inventarsystem / release-docker (push) Successful in 2m22s
This commit is contained in:
@@ -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">×</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
|
||||||
|
|||||||
Reference in New Issue
Block a user