Fix of an missing parethasys
Release Inventarsystem / release-docker (push) Successful in 2m15s

This commit is contained in:
2026-08-11 00:57:51 +02:00
parent e9006f5a07
commit 9f3799a77f
+444 -449
View File
@@ -1042,465 +1042,460 @@
}
async function returnByCode(code) {
if (!code) return;
setScanStatus('Verarbeite Rückgabe...', 'warn');
try {
const resp = await fetch('/api/library_return_by_code', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
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');
return false;
}
setScanStatus(result.message || `Zurückgegeben: ${result.item_name || ''}`, 'ok');
showSmallConfirm(result.message || `Zurückgegeben: ${result.item_name || ''}`, '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');
return false;
}
async function returnByCode(code) {
if (!code) return;
setScanStatus('Verarbeite Rückgabe...', 'warn');
try {
const resp = await fetch('/api/library_return_by_code', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
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');
return false;
}
setScanStatus(result.message || `Zurückgegeben: ${result.item_name || ''}`, 'ok');
showSmallConfirm(result.message || `Zurückgegeben: ${result.item_name || ''}`, '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');
return false;
}
}
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;
}
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
})
});
if (result.action === 'borrowed') {
setScanStatus(`Ausgeliehen: ${result.item_name}`, 'ok');
showSmallConfirm(`Ausgeliehen: ${result.item_name}`, 'ok');
} else if (result.action === 'returned') {
setScanStatus(`Zurückgegeben: ${result.item_name}`, 'ok');
showSmallConfirm(`Zurückgegeben: ${result.item_name}`, 'ok');
} else {
setScanStatus(result.message || 'Aktion durchgeführt.', 'ok');
showSmallConfirm(result.message || 'Aktion durchgeführt.', 'ok');
}
const result = await response.json();
if (!response.ok || !result.ok) {
setScanStatus(result.message || 'Scan-Aktion fehlgeschlagen.', 'error');
await loadLibraryItems();
} catch (err) {
console.error('Quick scan action failed:', err);
setScanStatus('Fehler beim Verarbeiten des Scans.', 'error');
}
}
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';
});
}
// =========================================================================
// 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 showSmallConfirm(message, kind='ok') {
// Append the small helper text in German
const helper = 'Sie können fortfahren. Dies ist nur eine kleine Benachrichtigung.';
const el = document.createElement('div');
el.className = `small-popup ${kind === 'error' ? 'error' : 'ok'}`;
el.innerHTML = `<div>${escapeHtml(String(message || ''))}</div><div style="opacity:0.9; margin-left:8px; font-size:0.85em;">${helper}</div><div class="close-x">&times;</div>`;
document.body.appendChild(el);
// close handler
el.querySelector('.close-x').addEventListener('click', () => {
if (el && el.parentNode) el.parentNode.removeChild(el);
});
// auto remove after 3 seconds
setTimeout(() => { try { if (el && el.parentNode) el.parentNode.removeChild(el); } catch(e){} }, 3000);
}
function setActiveStudentCard(cardId) {
activeStudentCardId = (cardId || '').trim().toUpperCase();
const input = document.getElementById('activeStudentCard');
if (input) input.value = activeStudentCardId;
}
function normalizeScannedCode(code) {
return (code || '').trim();
}
function getItemTypeLabel(type) {
const labels = { 'book': 'Buch', 'cd': 'CD', 'dvd': 'DVD', 'other': 'Sonstige', 'schoolbook': 'Schulbuch' };
return labels[type] || type;
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Opens the modal and fetches the data
function showItemDetail(itemId) {
const detailContent = document.getElementById('detailContent');
const detailModal = document.getElementById('detailModal');
// 1. Show the loading state immediately
detailContent.innerHTML = '<p>Loading details...</p>';
detailModal.style.display = 'flex';
// 2. Fetch the data
fetch(`/api/item_detail/${itemId}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.text();
})
.then(html => {
// 3. Clean the HTML and display it
detailContent.innerHTML = DOMPurify.sanitize(html);
})
.catch(err => {
console.error('Error loading detail:', err);
detailContent.innerHTML = '<p>Sorry, we could not load the item details. Please try again later.</p>';
});
}
// Closes the modal via the 'x' button
function closeDetailModal() {
document.getElementById('detailModal').style.display = 'none';
}
// Closes the modal if the user clicks the dark background overlay
window.onclick = function(event) {
const modal = document.getElementById('detailModal');
// If the click happened directly on the dark overlay (not the white box)
if (event.target === modal) {
closeDetailModal();
}
};
// =========================================================================
// 5. EVENT LISTENERS INITIALIZATION & ON-LOAD INITIALIZER
// =========================================================================
function wireScannerUi() {
const toggleBtn = document.getElementById('toggleScannerBtn');
const resetBtn = document.getElementById('resetCardBtn');
const modeSelect = document.getElementById('scanModeSelect');
const keyboardToggle = document.getElementById('keyboardScannerToggle');
if (toggleBtn) {
toggleBtn.addEventListener('click', async () => {
if (scannerRunning) {
await stopScanner();
} else {
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('Nur Ausweis-Modus aktiv.', 'warn');
}
});
}
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');
}
});
}
}
// Run when DOM structure is entirely ready
document.addEventListener('DOMContentLoaded', async () => {
wireScannerUi(); // Setup scanner control buttons
loadLibraryItems(); // Fetch your database items right away!
// Safely connect standard Filters and Search inputs inside DOMContentLoaded
const filterToggleBtn = document.getElementById('filterToggleBtn');
if (filterToggleBtn) {
filterToggleBtn.addEventListener('click', () => {
filterPanelOpen = !filterPanelOpen;
document.getElementById('filterPanel').classList.toggle('open');
filterToggleBtn.classList.toggle('active');
});
}
const applyFilterBtn = document.getElementById('applyFilterBtn');
if (applyFilterBtn) {
applyFilterBtn.addEventListener('click', () => {
activeFilters.isbn = document.getElementById('filterISBN').value.toLowerCase();
activeFilters.type = document.getElementById('filterType').value;
activeFilters.status = document.getElementById('filterStatus').value;
applyFiltersAndSearch(true);
});
}
const clearFilterBtn = document.getElementById('clearFilterBtn');
if (clearFilterBtn) {
clearFilterBtn.addEventListener('click', () => {
document.getElementById('filterISBN').value = '';
document.getElementById('filterType').value = '';
document.getElementById('filterStatus').value = '';
activeFilters = {isbn: '', type: '', status: ''};
applyFiltersAndSearch(true);
});
}
const librarySearch = document.getElementById('librarySearch');
if (librarySearch) {
librarySearch.addEventListener('input', (e) => {
currentSearchTerm = (e.target.value || '').toLowerCase();
applyFiltersAndSearch(true);
});
}
const loadMoreBtn = document.getElementById('loadMoreBtn');
if (loadMoreBtn) {
loadMoreBtn.addEventListener('click', () => {
renderedCount = Math.min(renderedCount + RENDER_BATCH_COUNT, visibleItems.length);
renderItems();
});
}
const manualReturnBtn = document.getElementById('manualReturnBtn');
const manualItemCode = document.getElementById('manualItemCode');
if (manualReturnBtn && manualItemCode) {
manualReturnBtn.addEventListener('click', async () => {
const code = (manualItemCode.value || '').trim();
if (!code) {
alert('Bitte einen Mediencode eingeben.');
return;
}
if (result.action === 'borrowed') {
setScanStatus(`Ausgeliehen: ${result.item_name}`, 'ok');
showSmallConfirm(`Ausgeliehen: ${result.item_name}`, 'ok');
} else if (result.action === 'returned') {
setScanStatus(`Zurückgegeben: ${result.item_name}`, 'ok');
showSmallConfirm(`Zurückgegeben: ${result.item_name}`, 'ok');
} else {
setScanStatus(result.message || 'Aktion durchgeführt.', 'ok');
showSmallConfirm(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 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';
await returnByCode(code);
});
}
// =========================================================================
// 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 showSmallConfirm(message, kind = 'ok') {
// Append the small helper text in German
const helper = 'Sie können fortfahren. Dies ist nur eine kleine Benachrichtigung.';
const el = document.createElement('div');
el.className = `small-popup ${kind === 'error' ? 'error' : 'ok'}`;
el.innerHTML = `<div>${escapeHtml(String(message || ''))}</div><div style="opacity:0.9; margin-left:8px; font-size:0.85em;">${helper}</div><div class="close-x">&times;</div>`;
document.body.appendChild(el);
// close handler
el.querySelector('.close-x').addEventListener('click', () => {
if (el && el.parentNode) el.parentNode.removeChild(el);
});
// auto remove after 3 seconds
setTimeout(() => {
try {
if (el && el.parentNode) el.parentNode.removeChild(el);
} catch (e) {
}
}, 3000);
}
function setActiveStudentCard(cardId) {
activeStudentCardId = (cardId || '').trim().toUpperCase();
const input = document.getElementById('activeStudentCard');
if (input) input.value = activeStudentCardId;
}
function normalizeScannedCode(code) {
return (code || '').trim();
}
function getItemTypeLabel(type) {
const labels = {'book': 'Buch', 'cd': 'CD', 'dvd': 'DVD', 'other': 'Sonstige', 'schoolbook': 'Schulbuch'};
return labels[type] || type;
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Opens the modal and fetches the data
function showItemDetail(itemId) {
const detailContent = document.getElementById('detailContent');
const detailModal = document.getElementById('detailModal');
// 1. Show the loading state immediately
detailContent.innerHTML = '<p>Loading details...</p>';
detailModal.style.display = 'flex';
// 2. Fetch the data
fetch(`/api/item_detail/${itemId}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.text();
})
.then(html => {
// 3. Clean the HTML and display it
detailContent.innerHTML = DOMPurify.sanitize(html);
})
.catch(err => {
console.error('Error loading detail:', err);
detailContent.innerHTML = '<p>Sorry, we could not load the item details. Please try again later.</p>';
});
}
// Closes the modal via the 'x' button
function closeDetailModal() {
document.getElementById('detailModal').style.display = 'none';
}
// Closes the modal if the user clicks the dark background overlay
window.onclick = function (event) {
const modal = document.getElementById('detailModal');
// If the click happened directly on the dark overlay (not the white box)
if (event.target === modal) {
closeDetailModal();
}
};
// =========================================================================
// 5. EVENT LISTENERS INITIALIZATION & ON-LOAD INITIALIZER
// =========================================================================
function wireScannerUi() {
const toggleBtn = document.getElementById('toggleScannerBtn');
const resetBtn = document.getElementById('resetCardBtn');
const modeSelect = document.getElementById('scanModeSelect');
const keyboardToggle = document.getElementById('keyboardScannerToggle');
if (toggleBtn) {
toggleBtn.addEventListener('click', async () => {
if (scannerRunning) {
await stopScanner();
} else {
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('Nur Ausweis-Modus aktiv.', 'warn');
}
});
}
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');
}
});
}
}
// Run when DOM structure is entirely ready
document.addEventListener('DOMContentLoaded', () => {
wireScannerUi(); // Setup scanner control buttons
loadLibraryItems(); // Fetch your database items right away!
// Safely connect standard Filters and Search inputs inside DOMContentLoaded
const filterToggleBtn = document.getElementById('filterToggleBtn');
if (filterToggleBtn) {
filterToggleBtn.addEventListener('click', () => {
filterPanelOpen = !filterPanelOpen;
document.getElementById('filterPanel').classList.toggle('open');
filterToggleBtn.classList.toggle('active');
});
}
const applyFilterBtn = document.getElementById('applyFilterBtn');
if (applyFilterBtn) {
applyFilterBtn.addEventListener('click', () => {
activeFilters.isbn = document.getElementById('filterISBN').value.toLowerCase();
activeFilters.type = document.getElementById('filterType').value;
activeFilters.status = document.getElementById('filterStatus').value;
applyFiltersAndSearch(true);
});
}
const clearFilterBtn = document.getElementById('clearFilterBtn');
if (clearFilterBtn) {
clearFilterBtn.addEventListener('click', () => {
document.getElementById('filterISBN').value = '';
document.getElementById('filterType').value = '';
document.getElementById('filterStatus').value = '';
activeFilters = {isbn: '', type: '', status: ''};
applyFiltersAndSearch(true);
});
}
const librarySearch = document.getElementById('librarySearch');
if (librarySearch) {
librarySearch.addEventListener('input', (e) => {
currentSearchTerm = (e.target.value || '').toLowerCase();
applyFiltersAndSearch(true);
});
}
const loadMoreBtn = document.getElementById('loadMoreBtn');
if (loadMoreBtn) {
loadMoreBtn.addEventListener('click', () => {
renderedCount = Math.min(renderedCount + RENDER_BATCH_COUNT, visibleItems.length);
renderItems();
});
}
const manualReturnBtn = document.getElementById('manualReturnBtn');
const manualItemCode = document.getElementById('manualItemCode');
if (manualReturnBtn && manualItemCode) {
manualReturnBtn.addEventListener('click', async () => {
const code = (manualItemCode.value || '').trim();
if (!code) {
alert('Bitte einen Mediencode eingeben.');
return;
}
await returnByCode(code);
});
}
const editForm = document.getElementById('editLibraryForm');
if (editForm) {
editForm.addEventListener('submit', async function (e) {
e.preventDefault();
const itemId = document.getElementById('editLibraryItemId').value;
const currentItem = libraryItems.find(i => i._id === itemId);
if (!currentItem) return;
const sharedPayload = {
name: document.getElementById('editLibraryName').value,
item_type: document.getElementById('editLibraryType').value,
isbn: document.getElementById('editLibraryIsbn').value,
ort: document.getElementById('editLibraryLocation').value,
beschreibung: document.getElementById('editLibraryDescription').value,
ansch_jahr: currentItem.Anschaffungsjahr || '',
ansch_kost: currentItem.Anschaffungskosten || '',
reservierbar: currentItem.Reservierbar !== false,
// Edit Modal Form processing
const editForm = document.getElementById('editLibraryForm');
if (editForm) {
try {
if (isGroupedEdit) {
const payload = {
series_group_id: currentItem.SeriesGroupId,
...sharedPayload,
items: groupMembers.map(member => ({
id: member._id,
code_4: codeByItemId.get(member._id) || ''
}))
};
const codeInputs = Array.from(document.querySelectorAll('#editLibraryCodesContainer input[data-item-id]'));
const codeByItemId = new Map(codeInputs.map(input => [input.dataset.itemId, (input.value || '').trim()]));
const groupMembers = editLibraryState.groupMembers.length > 0 ? editLibraryState.groupMembers : [currentItem];
const isGroupedEdit = Boolean(currentItem.SeriesGroupId) && groupMembers.length > 1;
const response = await fetch('/update_group', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}',
'X-CSRF-Token': '{{ csrf_token }}'
},
body: JSON.stringify(payload)
});
try {
if (isGroupedEdit) {
const payload = {
series_group_id: currentItem.SeriesGroupId,
...sharedPayload,
items: groupMembers.map(member => ({
id: member._id,
code_4: codeByItemId.get(member._id) || ''
}))
};
const response = await fetch('/update_group', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}',
'X-CSRF-Token': '{{ csrf_token }}'
},
body: JSON.stringify(payload)
});
const result = await response.json();
if (response.ok && result.success) {
alert(result.message || 'Gruppe erfolgreich aktualisiert!');
closeEditLibraryModal();
pagingState.loading = false;
await loadLibraryItems();
} else {
alert(result.message || 'Fehler beim Speichern der Gruppenänderungen.');
}
} else {
const primaryCodeInput = codeInputs[0];
const payload = {
name: sharedPayload.name,
item_type: sharedPayload.item_type,
isbn: sharedPayload.isbn,
code_4: primaryCodeInput ? primaryCodeInput.value.trim() : (currentItem.Code_4 || currentItem.Code4 || '').trim(),
ort: sharedPayload.ort,
beschreibung: sharedPayload.beschreibung
};
const response = await fetch(`/api/library_item/${itemId}/update`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}',
'X-CSRF-Token': '{{ csrf_token }}'
},
body: JSON.stringify(payload)
});
const result = await response.json();
if (response.ok && result.ok) {
alert(result.message || 'Medium erfolgreich aktualisiert!');
closeEditLibraryModal();
pagingState.loading = false;
await loadLibraryItems();
} else {
alert(result.message || 'Fehler beim Speichern der Änderungen.');
}
}
} catch (error) {
console.error('Update failed:', error);
alert('Netzwerkfehler beim Aktualisieren des Mediums.');
const result = await response.json();
if (response.ok && result.success) {
alert(result.message || 'Gruppe erfolgreich aktualisiert!');
closeEditLibraryModal();
pagingState.loading = false;
await loadLibraryItems();
} else {
alert(result.message || 'Fehler beim Speichern der Gruppenänderungen.');
}
});
}
});
} else {
const primaryCodeInput = codeInputs[0];
const payload = {
name: sharedPayload.name,
item_type: sharedPayload.item_type,
isbn: sharedPayload.isbn,
code_4: primaryCodeInput ? primaryCodeInput.value.trim() : (currentItem.Code_4 || currentItem.Code4 || '').trim(),
ort: sharedPayload.ort,
beschreibung: sharedPayload.beschreibung
};
async function fetchLibraryGroupMembers(seriesGroupId) {
if (!seriesGroupId) return [];
const response = await fetch(`/api/library_item/${itemId}/update`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}',
'X-CSRF-Token': '{{ csrf_token }}'
},
body: JSON.stringify(payload)
});
const result = await response.json();
if (response.ok && result.ok) {
alert(result.message || 'Medium erfolgreich aktualisiert!');
closeEditLibraryModal();
pagingState.loading = false;
await loadLibraryItems();
} else {
alert(result.message || 'Fehler beim Speichern der Änderungen.');
}
}
} catch (error) {
console.error('Update failed:', error);
alert('Netzwerkfehler beim Aktualisieren des Mediums.');
}
editForm.addEventListener('submit', async function (e) {
e.preventDefault();
const itemId = document.getElementById('editLibraryItemId').value;
const currentItem = libraryItems.find(i => i._id === itemId);
if (!currentItem) return;
const codeInputs = Array.from(document.querySelectorAll('#editLibraryCodesContainer input[data-item-id]'));
const sharedPayload = {
name: document.getElementById('editLibraryName').value,
item_type: document.getElementById('editLibraryType').value,
isbn: document.getElementById('editLibraryIsbn').value,
ort: document.getElementById('editLibraryLocation').value,
beschreibung: document.getElementById('editLibraryDescription').value,
ansch_jahr: currentItem.Anschaffungsjahr || '',
ansch_kost: currentItem.Anschaffungskosten || '',
reservierbar: currentItem.Reservierbar !== false,
};
const codeByItemId = new Map(codeInputs.map(input => [input.dataset.itemId, (input.value || '').trim()]));
const groupMembers = editLibraryState.groupMembers.length > 0 ? editLibraryState.groupMembers : [currentItem];
const isGroupedEdit = Boolean(currentItem.SeriesGroupId) && groupMembers.length > 1;
});
}
});
async function fetchLibraryGroupMembers(seriesGroupId) {
if (!seriesGroupId) return [];
try {
const response = await fetch(`/api/library_group/${encodeURIComponent(seriesGroupId)}`);
@@ -1595,9 +1590,9 @@
document.getElementById('editLibraryModal').style.display = 'flex';
};
function closeEditLibraryModal() {
document.getElementById('editLibraryModal').style.display = 'none';
}
function closeEditLibraryModal() {
document.getElementById('editLibraryModal').style.display = 'none';
}
}
</script>