fix: improve ISBN validation and update related messages in upload forms
This commit is contained in:
+10
-11
@@ -4801,16 +4801,15 @@ def upload_item():
|
||||
|
||||
item_isbn = ''
|
||||
item_type = 'general'
|
||||
if cfg.MODULES.is_enabled('library'):
|
||||
item_isbn = normalize_and_validate_isbn(isbn_raw)
|
||||
if isbn_raw and not item_isbn:
|
||||
error_msg = 'Ungültige ISBN. Bitte ISBN-10 oder ISBN-13 verwenden.'
|
||||
if is_mobile:
|
||||
return jsonify({'success': False, 'message': error_msg}), 400
|
||||
flash(error_msg, 'error')
|
||||
return redirect(url_for(success_redirect_endpoint))
|
||||
if item_isbn:
|
||||
if cfg.MODULES.is_enabled('library') and isbn_raw:
|
||||
normalized_isbn = normalize_and_validate_isbn(isbn_raw)
|
||||
if normalized_isbn:
|
||||
item_isbn = normalized_isbn
|
||||
item_type = 'book'
|
||||
else:
|
||||
item_isbn = isbn_raw
|
||||
if upload_mode == 'library':
|
||||
item_type = 'book'
|
||||
|
||||
if upload_mode == 'library':
|
||||
if not cfg.MODULES.is_enabled('library'):
|
||||
@@ -4819,8 +4818,8 @@ def upload_item():
|
||||
return jsonify({'success': False, 'message': error_msg}), 400
|
||||
flash(error_msg, 'error')
|
||||
return redirect(url_for(success_redirect_endpoint))
|
||||
if not item_isbn:
|
||||
error_msg = 'Für Bücher ist eine gültige ISBN erforderlich.'
|
||||
if not isbn_raw:
|
||||
error_msg = 'Für Bücher ist eine ISBN oder ein Barcode erforderlich.'
|
||||
if is_mobile:
|
||||
return jsonify({'success': False, 'message': error_msg}), 400
|
||||
flash(error_msg, 'error')
|
||||
|
||||
+26
-35
@@ -930,6 +930,29 @@
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-item.dropdown > .nav-link.dropdown-toggle,
|
||||
.user-menu-wrap > .user-menu-btn {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-item.dropdown > .dropdown-menu,
|
||||
.user-menu-wrap > .dropdown-menu {
|
||||
display: block !important;
|
||||
position: static !important;
|
||||
float: none !important;
|
||||
transform: none !important;
|
||||
inset: auto !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-item.dropdown > .dropdown-menu {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.user-menu-wrap > .dropdown-menu {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.navbar-nav .dropdown-item {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
padding: 12px 16px;
|
||||
@@ -974,6 +997,7 @@
|
||||
order: 3;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.function-search-form {
|
||||
@@ -2055,42 +2079,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Mobile menu is collapsed: avoid hiding links preemptively.
|
||||
if (navCollapse && !navCollapse.classList.contains('show')) {
|
||||
restoreAllNavItems();
|
||||
return;
|
||||
}
|
||||
|
||||
// Mobile burger menu should show the full list without overflow pruning.
|
||||
restoreAllNavItems();
|
||||
|
||||
const hiddenSources = [];
|
||||
let candidates = collectTopLevelNavSources();
|
||||
const overflowBuffer = 12;
|
||||
|
||||
while (isNavOverflowing(overflowBuffer) && candidates.length > 0) {
|
||||
const toHide = pickNextNavItemToHide(candidates);
|
||||
if (!toHide) {
|
||||
break;
|
||||
}
|
||||
toHide.style.display = 'none';
|
||||
hiddenSources.unshift(toHide);
|
||||
candidates = collectTopLevelNavSources();
|
||||
}
|
||||
|
||||
rebuildOverflowControl(hiddenSources);
|
||||
|
||||
// One final pass in case the overflow menu label/count itself changed row width.
|
||||
candidates = collectTopLevelNavSources();
|
||||
while (isNavOverflowing(overflowBuffer) && candidates.length > 0) {
|
||||
const toHide = pickNextNavItemToHide(candidates);
|
||||
if (!toHide) {
|
||||
break;
|
||||
}
|
||||
toHide.style.display = 'none';
|
||||
hiddenSources.unshift(toHide);
|
||||
rebuildOverflowControl(hiddenSources);
|
||||
candidates = collectTopLevelNavSources();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let resizeTimer = null;
|
||||
|
||||
@@ -883,7 +883,7 @@
|
||||
<div class="form-group">
|
||||
<label for="individual_codes">Einzelcodes (optional, je Zeile ein Code)</label>
|
||||
<textarea id="individual_codes" name="individual_codes" rows="4" placeholder="z.B.\nABC-001\nABC-002"></textarea>
|
||||
<small style="display:block; color:#666;">Bei Anzahl > 1 können hier individuelle Codes pro Item gesetzt werden. Scanner-Eingaben werden bei Mehrfachanzahl automatisch hier angefügt.</small>
|
||||
<small style="display:block; color:#666;">Bei Anzahl > 1 können hier individuelle Codes pro Item gesetzt werden. Der Scanner setzt immer zuerst den Basis-Code im Feld oben; weitere Einzelcodes können hier bei Bedarf manuell ergänzt werden.</small>
|
||||
</div>
|
||||
<!-- Image upload (hidden for library mode) -->
|
||||
<div class="form-group" {% if show_library_features %}style="display:none;"{% endif %}>
|
||||
@@ -904,7 +904,7 @@
|
||||
<button type="button" class="fetch-isbn-button" onclick="fetchBookInfo('upload')">Bild abrufen</button>
|
||||
</div>
|
||||
<div id="isbn-scanner" style="width:100%; max-width:520px; display:none; margin-top:10px;"></div>
|
||||
<small id="isbn-scan-status" style="display:block; color:#666; margin-top:6px;">Scannen oder manuell eingeben. Das Buchcover wird automatisch heruntergeladen.</small>
|
||||
<small id="isbn-scan-status" style="display:block; color:#666; margin-top:6px;">Scannen oder manuell eingeben. Gültige ISBNs helfen beim Abruf von Buchdaten, andere Codes werden trotzdem akzeptiert.</small>
|
||||
<div id="book-info-container" class="book-info-container"></div>
|
||||
</div>
|
||||
|
||||
@@ -965,33 +965,30 @@
|
||||
return '';
|
||||
}
|
||||
|
||||
function appendIndividualCode(scannedCode) {
|
||||
const textarea = document.getElementById('individual_codes');
|
||||
const itemCountInput = document.getElementById('item_count');
|
||||
if (!textarea || !itemCountInput) return false;
|
||||
function updateIsbnLiveValidation() {
|
||||
const isbnField = document.getElementById('isbn');
|
||||
const statusEl = document.getElementById('isbn-scan-status');
|
||||
if (!isbnField || !statusEl) return;
|
||||
|
||||
const itemCount = Math.max(1, parseInt(itemCountInput.value || '1', 10));
|
||||
if (itemCount <= 1) return false;
|
||||
const rawValue = isbnField.value.trim();
|
||||
isbnField.classList.remove('code-valid', 'code-invalid');
|
||||
|
||||
const existing = textarea.value
|
||||
.split(/\r?\n/)
|
||||
.map(line => line.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
if (existing.includes(scannedCode)) {
|
||||
setCode4ScanStatus(`Code ${scannedCode} ist bereits eingetragen.`, true);
|
||||
return true;
|
||||
if (!rawValue) {
|
||||
statusEl.textContent = 'Scannen oder manuell eingeben. Gültige ISBNs helfen beim Abruf von Buchdaten, andere Codes werden trotzdem akzeptiert.';
|
||||
statusEl.style.color = '#666';
|
||||
return;
|
||||
}
|
||||
|
||||
if (existing.length >= itemCount) {
|
||||
setCode4ScanStatus('Es sind bereits genug Einzelcodes für die gewählte Anzahl eingetragen.', true);
|
||||
return true;
|
||||
if (normalizeIsbnClient(rawValue)) {
|
||||
isbnField.classList.add('code-valid');
|
||||
statusEl.textContent = 'Gültiges ISBN-Format erkannt. Der Wert kann gespeichert werden.';
|
||||
statusEl.style.color = '#666';
|
||||
return;
|
||||
}
|
||||
|
||||
existing.push(scannedCode);
|
||||
textarea.value = existing.join('\n');
|
||||
setCode4ScanStatus(`Code ${scannedCode} hinzugefügt (${existing.length}/${itemCount}).`);
|
||||
return true;
|
||||
isbnField.classList.add('code-invalid');
|
||||
statusEl.textContent = 'Kein gültiges ISBN-10/13-Format erkannt. Der Wert wird trotzdem akzeptiert.';
|
||||
statusEl.style.color = '#b00020';
|
||||
}
|
||||
|
||||
function startCode4Scanner() {
|
||||
@@ -1027,7 +1024,7 @@
|
||||
rememberLastUsedCamera: true
|
||||
});
|
||||
code4ScannerRunning = true;
|
||||
setCode4ScanStatus('Scanner läuft. Mehrere Codes nacheinander möglich.');
|
||||
setCode4ScanStatus('Scanner läuft. Der Scan wird im Basis-Codefeld übernommen.');
|
||||
|
||||
code4ScannerInstance.render((decodedText) => {
|
||||
const scannedCode = String(decodedText || '').trim();
|
||||
@@ -1040,12 +1037,9 @@
|
||||
code4LastScanned = scannedCode;
|
||||
code4LastScannedAt = now;
|
||||
|
||||
const usedForIndividual = appendIndividualCode(scannedCode);
|
||||
if (!usedForIndividual) {
|
||||
codeField.value = scannedCode;
|
||||
validateCodeField(codeField);
|
||||
setCode4ScanStatus(`Code_4 gesetzt: ${scannedCode}`);
|
||||
}
|
||||
codeField.value = scannedCode;
|
||||
validateCodeField(codeField);
|
||||
setCode4ScanStatus(`Code_4 gesetzt: ${scannedCode}`);
|
||||
}, () => {});
|
||||
}
|
||||
|
||||
@@ -1094,21 +1088,23 @@
|
||||
if (!scannedCode) return;
|
||||
|
||||
const normalizedIsbn = normalizeIsbnClient(scannedCode);
|
||||
if (!normalizedIsbn) {
|
||||
setIsbnScanStatus('Kein gültiger ISBN-Barcode erkannt. Bitte erneut scannen.', true);
|
||||
return;
|
||||
isbnField.value = normalizedIsbn || scannedCode;
|
||||
updateIsbnLiveValidation();
|
||||
if (normalizedIsbn) {
|
||||
setIsbnScanStatus(`ISBN gesetzt: ${normalizedIsbn}`);
|
||||
} else {
|
||||
setIsbnScanStatus('Kein gültiges ISBN-Format erkannt, der gescannte Wert bleibt aber im Feld.', true);
|
||||
}
|
||||
|
||||
isbnField.value = normalizedIsbn;
|
||||
setIsbnScanStatus(`ISBN gesetzt: ${normalizedIsbn}`);
|
||||
|
||||
isbnScannerInstance.clear().catch(() => {});
|
||||
scannerBox.style.display = 'none';
|
||||
scanButton.textContent = 'ISBN scannen';
|
||||
isbnScannerRunning = false;
|
||||
|
||||
// Automatically load book metadata after scan.
|
||||
fetchBookInfo('upload');
|
||||
if (normalizedIsbn) {
|
||||
// Automatically load book metadata after a valid ISBN scan.
|
||||
fetchBookInfo('upload');
|
||||
}
|
||||
}, () => {});
|
||||
}
|
||||
|
||||
@@ -1231,7 +1227,7 @@
|
||||
const isbn = normalizeIsbnClient(isbnField.value);
|
||||
|
||||
if (!isbn) {
|
||||
alert('Bitte eine gültige ISBN-10 oder ISBN-13 eingeben.');
|
||||
infoContainer.innerHTML = '<div class="error-message">Bitte geben Sie eine ISBN oder einen Barcode ein.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1645,6 +1641,13 @@
|
||||
if (scanIsbnBtn) {
|
||||
scanIsbnBtn.addEventListener('click', startIsbnScanner);
|
||||
}
|
||||
|
||||
const isbnField = document.getElementById('isbn');
|
||||
if (isbnField) {
|
||||
isbnField.addEventListener('input', updateIsbnLiveValidation);
|
||||
isbnField.addEventListener('blur', updateIsbnLiveValidation);
|
||||
updateIsbnLiveValidation();
|
||||
}
|
||||
|
||||
// Setup add new location button
|
||||
const addLocationBtn = document.getElementById('add-new-location-btn');
|
||||
|
||||
Reference in New Issue
Block a user