Compare commits

...

4 Commits

+337 -373
View File
@@ -549,9 +549,12 @@
</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>
// State
// =========================================================================
// 1. GLOBAL STATE DEFINITIONS
// =========================================================================
let libraryItems = [];
let filteredItems = [];
let visibleItems = [];
@@ -573,13 +576,20 @@
const RENDER_BATCH_COUNT = 120;
let renderedCount = INITIAL_RENDER_COUNT;
let filterPanelOpen = false;
// Scanner Related State Variables
let scannerInstance = null;
let scannerRunning = false;
let scannerRunning = false;
let activeScannerCallback = null;
let activeStudentCardId = '';
let lastScanValue = '';
let lastScanAt = 0;
const canEditLibraryItems = (document.getElementById('libraryTableContainer')?.dataset.canEdit === '1');
// =========================================================================
// 2. DATA LOADING & FILTERING ENGINE
// =========================================================================
async function fetchLibraryPage(offset, limit) {
const response = await fetch(`/api/library_items?offset=${offset}&limit=${limit}`);
if (!response.ok) {
@@ -588,7 +598,6 @@
return response.json();
}
// Load items progressively: first page immediately, rest in background
async function loadLibraryItems() {
if (pagingState.loading) return;
pagingState.loading = true;
@@ -627,7 +636,6 @@
pagingState.total = page.total || pagingState.total;
pagingState.hasMore = Boolean(page.has_more);
// Keep the current view reactive while avoiding a full rerender burst.
applyFiltersAndSearch(false);
if ('requestIdleCallback' in window) {
@@ -679,7 +687,6 @@
renderItems();
}
// Display current view with incremental rendering
function renderItems() {
const tbody = document.getElementById('itemsTableBody');
const emptyState = document.getElementById('emptyState');
@@ -697,37 +704,34 @@
emptyState.style.display = 'none';
const rowsToRender = visibleItems.slice(0, renderedCount);
tbody.innerHTML = rowsToRender.map(item => `
${(() => {
const statusKey = item.LibraryDisplayStatus || (item.Verfuegbar === false ? 'borrowed' : 'available');
const statusClass = statusKey === 'damaged' ? 'status-damaged' : (statusKey === 'borrowed' ? 'status-borrowed' : 'status-available');
const statusText = statusKey === 'damaged' ? 'Defekt/Zerstört' : (statusKey === 'borrowed' ? 'Ausgeliehen' : 'Verfügbar');
const actionLabel = statusKey === 'available' ? 'Ausleihen' : (statusKey === 'borrowed' ? 'Reservieren' : 'Nicht ausleihbar');
const actionDisabled = statusKey === 'damaged' ? 'disabled' : '';
return `
<tr>
<td class="table-title">${escapeHtml(item.Name || 'Untitled')}</td>
<td>${escapeHtml(item.Autor || item.Author || '-')}</td>
<td>${escapeHtml(item.ISBN || item.Code_4 || item.Code4 || '-')}</td>
<td>${getItemTypeLabel(item.ItemType || 'book')}</td>
<td style="font-weight:600; text-align:center;">${item.Quantity || item.GroupedDisplayCount || 1}</td>
<td>
<span class="table-status ${statusClass}">
${statusText}
</span>
</td>
<td class="table-actions">
<button class="button" onclick="showItemDetail('${item._id}')">Details</button>
<button class="button" style="background: #28a745; color: white;" onclick="borrowItem('${item._id}')" ${actionDisabled}>
${actionLabel}
</button>
${canEditLibraryItems ? `<button class="button" style="background:#0ea5e9;color:#fff;" onclick="openEditLibraryItem('${item._id}')">Bearbeiten</button>` : ''}
${canEditLibraryItems ? `<button class="button" style="background:#dc2626;color:#fff; margin-left:6px;" onclick="confirmDeleteLibraryItem('${item._id}')">Löschen</button>` : ''}
</td>
</tr>
`;
})()}
`).join('');
tbody.innerHTML = rowsToRender.map(item => {
const statusKey = item.LibraryDisplayStatus || (item.Verfuegbar === false ? 'borrowed' : 'available');
const statusClass = statusKey === 'damaged' ? 'status-damaged' : (statusKey === 'borrowed' ? 'status-borrowed' : 'status-available');
const statusText = statusKey === 'damaged' ? 'Defekt/Zerstört' : (statusKey === 'borrowed' ? 'Ausgeliehen' : 'Verfügbar');
const actionLabel = statusKey === 'available' ? 'Ausleihen' : (statusKey === 'borrowed' ? 'Reservieren' : 'Nicht ausleihbar');
const actionDisabled = statusKey === 'damaged' ? 'disabled' : '';
return `
<tr>
<td class="table-title">${escapeHtml(item.Name || 'Untitled')}</td>
<td>${escapeHtml(item.Autor || item.Author || '-')}</td>
<td>${escapeHtml(item.ISBN || item.Code_4 || item.Code4 || '-')}</td>
<td>${getItemTypeLabel(item.ItemType || 'book')}</td>
<td style="font-weight:600; text-align:center;">${item.Quantity || item.GroupedDisplayCount || 1}</td>
<td>
<span class="table-status ${statusClass}">
${statusText}
</span>
</td>
<td class="table-actions">
<button class="button" onclick="showItemDetail('${item._id}')">Details</button>
<button class="button" style="background: #28a745; color: white;" onclick="borrowItem('${item._id}')" ${actionDisabled}>
${actionLabel}
</button>
${canEditLibraryItems ? `<button class="button" style="background:#0ea5e9;color:#fff;" onclick="openEditLibraryItem('${item._id}')">Bearbeiten</button>` : ''}
${canEditLibraryItems ? `<button class="button" style="background:#dc2626;color:#fff; margin-left:6px;" onclick="confirmDeleteLibraryItem('${item._id}')">Löschen</button>` : ''}
</td>
</tr>`;
}).join('');
const canLoadMoreRows = renderedCount < visibleItems.length;
if (canLoadMoreRows) {
@@ -742,13 +746,6 @@
}
}
// Confirm and call library item delete endpoint
function confirmDeleteLibraryItem(itemId) {
if (!itemId) return;
if (!confirm('Dieses Bibliotheksmedium revisionssicher löschen? Diese Aktion deaktiviert alle zugehörigen Exemplare und archiviert Medien.')) return;
deleteLibraryItem(itemId);
}
async function deleteLibraryItem(itemId) {
if (!itemId) return;
document.body.style.cursor = 'wait';
@@ -772,7 +769,6 @@
}
loadLibraryItems();
} else {
// Server redirected to HTML (flash messages). Reload to show state.
location.reload();
}
} catch (e) {
@@ -783,46 +779,269 @@
}
}
// Filter toggle
document.getElementById('filterToggleBtn').addEventListener('click', () => {
filterPanelOpen = !filterPanelOpen;
const panel = document.getElementById('filterPanel');
const btn = document.getElementById('filterToggleBtn');
panel.classList.toggle('open');
btn.classList.toggle('active');
function confirmDeleteLibraryItem(itemId) {
if (!itemId) return;
if (!confirm('Dieses Bibliotheksmedium revisionssicher löschen? Diese Aktion deaktiviert alle zugehörigen Exemplare und archiviert Medien.')) return;
deleteLibraryItem(itemId);
}
// =========================================================================
// 3. CORE SCANNER ROUTING ENGINE (QUAGGA2)
// =========================================================================
function startScanner(targetCallback) {
const readerWrap = document.getElementById('scanReaderWrap');
const toggleBtn = document.getElementById('toggleScannerBtn');
activeScannerCallback = targetCallback;
if (readerWrap) readerWrap.style.display = 'block';
setScanStatus('Initializing camera...', 'warn');
Quagga.init({
inputStream: {
name: "Live",
type: "LiveStream",
target: document.querySelector('#library-scanner-container'),
constraints: {
width: 640,
height: 480,
facingMode: "environment"
},
},
decoder: {
readers: [
"code_128_reader",
"ean_reader",
"code_39_reader",
"upc_reader",
"codabar_reader",
"i2of5_reader"
]
}
}, function(err) {
if (err) {
console.error('Scanner start failed:', err);
if (readerWrap) readerWrap.style.display = 'none';
const detail = (err && (err.message || err.name)) ? ` (${err.message || err.name})` : '';
setScanStatus(`Scanner konnte nicht gestartet werden${detail}`, 'error');
return;
}
Quagga.start();
scannerRunning = true;
if (!targetCallback && toggleBtn) {
toggleBtn.textContent = 'Scanner stoppen';
}
setScanStatus('Scanner aktiv. Jetzt Code scannen.', 'warn');
});
}
function stopScanner() {
if (!scannerRunning) return;
const readerWrap = document.getElementById('scanReaderWrap');
const toggleBtn = document.getElementById('toggleScannerBtn');
Quagga.stop();
scannerRunning = false;
activeScannerCallback = null;
if (readerWrap) readerWrap.style.display = 'none';
if (toggleBtn) toggleBtn.textContent = 'Scanner starten';
setScanStatus('Scanner gestoppt.', 'warn');
}
Quagga.onDetected(function(data) {
if (!data || !data.codeResult || !data.codeResult.code) return;
const barcode = String(data.codeResult.code || '').trim();
console.log("Barcode detected:", barcode);
const currentCallback = activeScannerCallback;
stopScanner();
if (typeof currentCallback === "function") {
currentCallback(barcode);
} else {
handleScanSuccess(barcode);
}
});
// Apply filters
document.getElementById('applyFilterBtn').addEventListener('click', () => {
activeFilters.author = document.getElementById('filterAuthor').value.toLowerCase();
activeFilters.isbn = document.getElementById('filterISBN').value.toLowerCase();
activeFilters.type = document.getElementById('filterType').value;
activeFilters.status = document.getElementById('filterStatus').value;
applyFiltersAndSearch(true);
});
function handleScanSuccess(decodedText) {
const scannedCode = normalizeScannedCode(decodedText);
if (!scannedCode) return;
const now = Date.now();
if (scannedCode === lastScanValue && (now - lastScanAt) < 1500) {
return;
}
lastScanValue = scannedCode;
lastScanAt = now;
const mode = (document.getElementById('scanModeSelect') || {}).value || 'card_only';
if (mode === 'card_only') {
setActiveStudentCard(scannedCode);
setScanStatus(`Ausweis gesetzt: ${activeStudentCardId}`, 'ok');
return;
}
processQuickToggleScan(scannedCode);
}
// Clear filters
document.getElementById('clearFilterBtn').addEventListener('click', () => {
document.getElementById('filterAuthor').value = '';
document.getElementById('filterISBN').value = '';
document.getElementById('filterType').value = '';
document.getElementById('filterStatus').value = '';
activeFilters = { author: '', isbn: '', type: '', status: '' };
applyFiltersAndSearch(true);
});
async function processQuickToggleScan(scannedCode) {
if (!activeStudentCardId) {
setActiveStudentCard(scannedCode);
setScanStatus(`Ausweis gesetzt: ${activeStudentCardId}`, 'ok');
return;
}
try {
setScanStatus('Verarbeite Mediencode...', 'warn');
const response = await fetch('/api/library_scan_action', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
student_card_id: activeStudentCardId,
item_code: scannedCode
})
});
const result = await response.json();
if (!response.ok || !result.ok) {
setScanStatus(result.message || 'Scan-Aktion fehlgeschlagen.', 'error');
return;
}
if (result.action === 'borrowed') {
setScanStatus(`Ausgeliehen: ${result.item_name}`, 'ok');
} else if (result.action === 'returned') {
setScanStatus(`Zurückgegeben: ${result.item_name}`, 'ok');
} else {
setScanStatus(result.message || 'Aktion durchgeführt.', 'ok');
}
await loadLibraryItems();
} catch (err) {
console.error('Quick scan action failed:', err);
setScanStatus('Fehler beim Verarbeiten des Scans.', 'error');
}
}
// Search input
document.getElementById('librarySearch').addEventListener('input', (e) => {
currentSearchTerm = (e.target.value || '').toLowerCase();
applyFiltersAndSearch(true);
});
function scanIntoEditCode() {
const scanReaderWrap = document.getElementById('scanReaderWrap');
const editCodeInput = document.getElementById('edit-code4');
const scanEditBtn = document.getElementById('scan-edit-code-btn');
if (!scanReaderWrap || !editCodeInput || !scanEditBtn) {
return;
}
if (scannerRunning && scanReaderWrap.style.display !== 'none') {
stopScanner();
scanEditBtn.textContent = 'Barcode scannen';
return;
}
scanEditBtn.textContent = 'Scanner schließen';
startScanner(function(decodedText) {
editCodeInput.value = decodedText;
if(typeof validateCodeField === "function") {
validateCodeField(editCodeInput, document.getElementById('edit-item-id')?.value || null);
}
scanEditBtn.textContent = 'Barcode scannen';
});
}
document.getElementById('loadMoreBtn').addEventListener('click', () => {
renderedCount = Math.min(renderedCount + RENDER_BATCH_COUNT, visibleItems.length);
renderItems();
});
// =========================================================================
// 4. UI INTERACTIONS & UTILITIES
// =========================================================================
function borrowItem(itemId) {
const selectedItem = (libraryItems || []).find(item => item._id === itemId);
if (selectedItem && selectedItem.LibraryDisplayStatus === 'damaged') {
alert('Dieses Medium ist als defekt/zerstört markiert und kann nicht ausgeliehen werden.');
return;
}
const defaultCardId = activeStudentCardId || '';
const cardId = (window.prompt('Bitte Schülerausweis-ID eingeben:', defaultCardId) || '').trim().toUpperCase();
if (!cardId) {
alert('Ausleihe abgebrochen: Für Bibliotheksmedien ist eine gültige Schülerausweis-ID erforderlich.');
return;
}
setActiveStudentCard(cardId);
const durationInput = (window.prompt('Ausleihdauer in Tagen (optional):') || '').trim();
const maxAvailable = Math.max(1, parseInt(selectedItem?.AvailableGroupedCount || selectedItem?.Quantity || 1, 10) || 1);
const countPrompt = (window.prompt(`Anzahl ausleihen? (Standard: 1, verfügbar: ${maxAvailable})`, '1') || '').trim();
let borrowCount = parseInt(countPrompt || '1', 10);
if (!Number.isFinite(borrowCount) || borrowCount < 1) {
borrowCount = 1;
}
if (borrowCount > maxAvailable) {
alert(`Es sind nur ${maxAvailable} Exemplar(e) verfügbar.`);
return;
}
const form = document.createElement('form');
form.method = 'POST';
form.action = `/ausleihen/${itemId}`;
const csrfField = document.createElement('input');
csrfField.type = 'hidden';
csrfField.name = 'csrf_token';
csrfField.value = '{{ csrf_token }}';
form.appendChild(csrfField);
const cardField = document.createElement('input');
cardField.type = 'hidden';
cardField.name = 'borrower_card_id';
cardField.value = cardId;
form.appendChild(cardField);
const returnTargetField = document.createElement('input');
returnTargetField.type = 'hidden';
returnTargetField.name = 'return_to';
returnTargetField.value = 'library';
form.appendChild(returnTargetField);
if (durationInput) {
const durationField = document.createElement('input');
durationField.type = 'hidden';
durationField.name = 'borrow_duration_days';
durationField.value = durationInput;
form.appendChild(durationField);
}
const countField = document.createElement('input');
countField.type = 'hidden';
countField.name = 'exemplare_count';
countField.value = String(borrowCount || 1);
form.appendChild(countField);
document.body.appendChild(form);
form.submit();
}
function setScanStatus(message, kind = '') {
const el = document.getElementById('scanStatus');
if (!el) return;
el.textContent = message;
el.classList.remove('ok', 'warn', 'error');
if (kind) el.classList.add(kind);
}
function setActiveStudentCard(cardId) {
activeStudentCardId = (cardId || '').trim().toUpperCase();
const input = document.getElementById('activeStudentCard');
if (input) input.value = activeStudentCardId;
}
function normalizeScannedCode(code) {
return (code || '').trim();
}
// Helper functions
function getItemTypeLabel(type) {
const labels = { 'book': 'Buch', 'cd': 'CD', 'dvd': 'DVD', 'other': 'Sonstige' };
return labels[type] || type;
@@ -835,7 +1054,6 @@
}
function showItemDetail(itemId) {
// Load and show detail modal
fetch(`/api/item_detail/${itemId}`)
.then(r => r.text())
.then(html => {
@@ -849,334 +1067,80 @@
document.getElementById('detailModal').style.display = 'none';
}
// Keep track of the scanner state globally/outer scope
let scannerRunning = false;
let lastScanValue = null;
let lastScanAt = 0;
let activeStudentCardId = ''; // Managed globally by your system
function borrowItem(itemId) {
const selectedItem = (libraryItems || []).find(item => item._id === itemId);
if (selectedItem && selectedItem.LibraryDisplayStatus === 'damaged') {
alert('Dieses Medium ist als defekt/zerstör markiert und kann nicht ausgeliehen werden.');
return;
}
const defaultCardId = activeStudentCardId || '';
const cardId = (window.prompt('Bitte Schülerausweis-ID eingeben:', defaultCardId) || '').trim().toUpperCase();
if (!cardId) {
alert('Ausleihe abgebrochen: Für Bibliotheksmedien ist eine gültige Schülerausweis-ID erforderlich.');
return;
}
setActiveStudentCard(cardId);
const durationInput = (window.prompt('Ausleihdauer in Tagen (optional):') || '').trim();
const maxAvailable = Math.max(1, parseInt(selectedItem?.AvailableGroupedCount || selectedItem?.Quantity || 1, 10) || 1);
const countPrompt = (window.prompt(`Anzahl ausleihen? (Standard: 1, verfügbar: ${maxAvailable})`, '1') || '').trim();
let borrowCount = parseInt(countPrompt || '1', 10);
if (!Number.isFinite(borrowCount) || borrowCount < 1) {
borrowCount = 1;
}
if (borrowCount > maxAvailable) {
alert(`Es sind nur ${maxAvailable} Exemplar(e) verfügbar.`);
return;
}
const form = document.createElement('form');
form.method = 'POST';
form.action = `/ausleihen/${itemId}`;
const csrfField = document.createElement('input');
csrfField.type = 'hidden';
csrfField.name = 'csrf_token';
csrfField.value = '{{ csrf_token }}';
form.appendChild(csrfField);
const cardField = document.createElement('input');
cardField.type = 'hidden';
cardField.name = 'borrower_card_id';
cardField.value = cardId;
form.appendChild(cardField);
const returnTargetField = document.createElement('input');
returnTargetField.type = 'hidden';
returnTargetField.name = 'return_to';
returnTargetField.value = 'library';
form.appendChild(returnTargetField);
if (durationInput) {
const durationField = document.createElement('input');
durationField.type = 'hidden';
durationField.name = 'borrow_duration_days';
durationField.value = durationInput;
form.appendChild(durationField);
}
const countField = document.createElement('input');
countField.type = 'hidden';
countField.name = 'exemplare_count';
countField.value = String(borrowCount || 1);
form.appendChild(countField);
document.body.appendChild(form);
form.submit();
}
function setScanStatus(message, kind = '') {
const el = document.getElementById('scanStatus');
if (!el) return;
el.textContent = message;
el.classList.remove('ok', 'warn', 'error');
if (kind) {
el.classList.add(kind);
}
}
function setActiveStudentCard(cardId) {
activeStudentCardId = (cardId || '').trim().toUpperCase();
const input = document.getElementById('activeStudentCard');
if (input) {
input.value = activeStudentCardId;
}
}
function normalizeScannedCode(code) {
return (code || '').trim();
}
async function processQuickToggleScan(scannedCode) {
if (!activeStudentCardId) {
setActiveStudentCard(scannedCode);
setScanStatus(`Ausweis gesetzt: ${activeStudentCardId}`, 'ok');
return;
}
try {
setScanStatus('Verarbeite Mediencode...', 'warn');
const response = await fetch('/api/library_scan_action', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
student_card_id: activeStudentCardId,
item_code: scannedCode
})
});
const result = await response.json();
if (!response.ok || !result.ok) {
setScanStatus(result.message || 'Scan-Aktion fehlgeschlagen.', 'error');
return;
}
if (result.action === 'borrowed') {
setScanStatus(`Ausgeliehen: ${result.item_name}`, 'ok');
} else if (result.action === 'returned') {
setScanStatus(`Zurückgegeben: ${result.item_name}`, 'ok');
} else {
setScanStatus(result.message || 'Aktion durchgeführt.', 'ok');
}
await loadLibraryItems();
} catch (err) {
console.error('Quick scan action failed:', err);
setScanStatus('Fehler beim Verarbeiten des Scans.', 'error');
}
}
function handleScanSuccess(decodedText) {
const scannedCode = normalizeScannedCode(decodedText);
if (!scannedCode) return;
const now = Date.now();
if (scannedCode === lastScanValue && (now - lastScanAt) < 1500) {
return;
}
lastScanValue = scannedCode;
lastScanAt = now;
const mode = (document.getElementById('scanModeSelect') || {}).value || 'card_only';
if (mode === 'card_only') {
setActiveStudentCard(scannedCode);
setScanStatus(`Ausweis gesetzt: ${activeStudentCardId}`, 'ok');
return;
}
processQuickToggleScan(scannedCode);
}
// Global Quagga reader hook
Quagga.onDetected(function(data) {
if (data && data.codeResult && data.codeResult.code) {
handleScanSuccess(data.codeResult.code);
}
});
async function startScanner() {
if (scannerRunning) return;
const readerWrap = document.getElementById('scanReaderWrap');
const toggleBtn = document.getElementById('toggleScannerBtn');
if (readerWrap) readerWrap.style.display = 'block';
setScanStatus('Initializing camera...', 'warn');
Quagga.init({
inputStream: {
name: "Live",
type: "LiveStream",
// Points to the interior viewport layout box
target: document.querySelector('#libraryQrReader'),
constraints: {
width: 640,
height: 480,
facingMode: "environment" // Force rear mobile lenses
},
},
decoder: {
// Limited down precisely to standard library/ID configurations
readers: [
"code_128_reader",
"ean_reader",
"code_39_reader",
"upc_reader",
"codabar_reader",
"i2of5_reader" // Matches original 'ITF' format mapping
]
}
}, function(err) {
if (err) {
console.error('Scanner start failed:', err);
if (readerWrap) readerWrap.style.display = 'none';
const detail = (err && (err.message || err.name)) ? ` (${err.message || err.name})` : '';
setScanStatus(`Scanner konnte nicht gestartet werden${detail}`, 'error');
return;
}
Quagga.start();
scannerRunning = true;
if (toggleBtn) toggleBtn.textContent = 'Scanner stoppen';
setScanStatus('Scanner aktiv. Jetzt Code scannen.', 'warn');
});
}
async function stopScanner() {
if (!scannerRunning) return;
const readerWrap = document.getElementById('scanReaderWrap');
const toggleBtn = document.getElementById('toggleScannerBtn');
Quagga.stop();
scannerRunning = false;
if (readerWrap) readerWrap.style.display = 'none';
if (toggleBtn) toggleBtn.textContent = 'Scanner starten';
setScanStatus('Scanner gestoppt.', 'warn');
}
// =========================================================================
// 5. EVENT LISTENERS INITIALIZATION & ON-LOAD INITIALIZER
// =========================================================================
function wireScannerUi() {
const toggleBtn = document.getElementById('toggleScannerBtn');
const resetBtn = document.getElementById('resetCardBtn');
const modeSelect = document.getElementById('scanModeSelect');
if (toggleBtn) {
toggleBtn.addEventListener('click', async () => {
if (scannerRunning) {
await stopScanner();
} else {
await startScanner();
await startScanner(null);
}
});
}
if (resetBtn) {
resetBtn.addEventListener('click', () => {
setActiveStudentCard('');
setScanStatus('Ausweis zurückgesetzt. Bitte neu scannen.', 'warn');
});
}
if (modeSelect) {
modeSelect.addEventListener('change', () => {
if (modeSelect.value === 'quick_toggle' && !activeStudentCardId) {
setScanStatus('Schnellmodus: zuerst Schülerausweis scannen.', 'warn');
} else if (modeSelect.value === 'card_only') {
setScanStatus('Modus aktiv: Nur Ausweis erfassen.', 'warn');
} else {
setScanStatus('Schnellmodus aktiv: jetzt Mediencode scannen.', 'warn');
setScanStatus('Nur Ausweis-Modus aktiv.', 'warn');
}
});
}
}
function getLibraryItemById(itemId) {
return libraryItems.find(i => String(i._id) === String(itemId));
}
// Connect standard Filters and Search inputs
document.getElementById('filterToggleBtn').addEventListener('click', () => {
filterPanelOpen = !filterPanelOpen;
document.getElementById('filterPanel').classList.toggle('open');
document.getElementById('filterToggleBtn').classList.toggle('active');
});
function openEditLibraryItem(itemId) {
const item = getLibraryItemById(itemId);
if (!item) {
alert('Element nicht gefunden.');
return;
}
document.getElementById('applyFilterBtn').addEventListener('click', () => {
activeFilters.author = document.getElementById('filterAuthor').value.toLowerCase();
activeFilters.isbn = document.getElementById('filterISBN').value.toLowerCase();
activeFilters.type = document.getElementById('filterType').value;
activeFilters.status = document.getElementById('filterStatus').value;
applyFiltersAndSearch(true);
});
const modal = document.getElementById('editLibraryModal');
document.getElementById('editLibraryItemId').value = item._id || '';
document.getElementById('editLibraryName').value = item.Name || '';
document.getElementById('editLibraryAuthor').value = item.Autor || item.Author || '';
document.getElementById('editLibraryDescription').value = item.Beschreibung || '';
document.getElementById('editLibraryLocation').value = item.Ort || '';
document.getElementById('editLibraryIsbn').value = item.ISBN || '';
document.getElementById('editLibraryCode4').value = item.Code_4 || '';
document.getElementById('editLibraryType').value = (item.ItemType || 'book');
modal.style.display = 'flex';
}
document.getElementById('clearFilterBtn').addEventListener('click', () => {
document.getElementById('filterAuthor').value = '';
document.getElementById('filterISBN').value = '';
document.getElementById('filterType').value = '';
document.getElementById('filterStatus').value = '';
activeFilters = { author: '', isbn: '', type: '', status: '' };
applyFiltersAndSearch(true);
});
function closeEditLibraryModal() {
const modal = document.getElementById('editLibraryModal');
modal.style.display = 'none';
}
document.getElementById('librarySearch').addEventListener('input', (e) => {
currentSearchTerm = (e.target.value || '').toLowerCase();
applyFiltersAndSearch(true);
});
async function submitEditLibraryItem(event) {
event.preventDefault();
const itemId = document.getElementById('editLibraryItemId').value;
const payload = {
name: document.getElementById('editLibraryName').value,
autor: document.getElementById('editLibraryAuthor').value,
beschreibung: document.getElementById('editLibraryDescription').value,
ort: document.getElementById('editLibraryLocation').value,
isbn: document.getElementById('editLibraryIsbn').value,
code_4: document.getElementById('editLibraryCode4').value,
item_type: document.getElementById('editLibraryType').value
};
document.getElementById('loadMoreBtn').addEventListener('click', () => {
renderedCount = Math.min(renderedCount + RENDER_BATCH_COUNT, visibleItems.length);
renderItems();
});
try {
const response = await fetch(`/api/library_item/${itemId}/update`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload)
});
const result = await response.json();
if (!response.ok || !result.ok) {
alert(result.message || 'Bearbeiten fehlgeschlagen.');
return;
}
closeEditLibraryModal();
await loadLibraryItems();
setScanStatus('Bibliotheksmedium aktualisiert.', 'ok');
} catch (err) {
console.error('Library edit failed:', err);
alert('Fehler beim Bearbeiten des Bibliotheksmediums.');
}
}
// Load on page init
// Run when DOM structure is entirely ready
document.addEventListener('DOMContentLoaded', () => {
loadLibraryItems();
wireScannerUi();
const editForm = document.getElementById('editLibraryForm');
if (editForm) {
editForm.addEventListener('submit', submitEditLibraryItem);
}
wireScannerUi(); // Setup scanner control buttons
loadLibraryItems(); // Fetch your database items right away!
});
</script>