Files
Inventarsystem/Web/templates/edit_library.html
T
Aiirondev_dev 4917c22ae3
Release Inventarsystem / release-docker (push) Successful in 2m14s
Debug changes
2026-08-11 22:49:40 +02:00

1004 lines
32 KiB
HTML

<!--
Copyright 2025-2026 AIIrondev
Licensed under the Inventarsystem EULA (Endbenutzer-Lizenzvertrag).
See Legal/LICENSE for the full license text.
Unauthorized commercial use, SaaS hosting, or removal of branding is prohibited.
For commercial licensing inquiries: https://github.com/AIIrondev
-->
{% extends "base.html" %}
{% block title %}{{ page_title|default('Artikel bearbeiten') }} - Inventarsystem{% endblock %}
{% block content %}
<script>
// Pre-initialize duplicate data from server
var serverDuplicateData = null;
const dataElement = document.getElementById('server-duplicate-data');
if (dataElement) {
// Safely parse JSON arrays with error handling
function safeJsonParse(jsonStr, defaultValue = []) {
try {
if (!jsonStr) return defaultValue;
return JSON.parse(jsonStr);
} catch (e) {
console.error("Error parsing JSON:", e, "Value was:", jsonStr);
return defaultValue;
}
}
// Get images data and log it for debugging
const imagesData = dataElement.getAttribute('data-images') || "[]";
console.log("Raw images data attribute:", imagesData);
serverDuplicateData = {
name: dataElement.getAttribute('data-name') || "",
description: dataElement.getAttribute('data-description') || "",
location: dataElement.getAttribute('data-location') || "",
room: dataElement.getAttribute('data-room') || "",
year: dataElement.getAttribute('data-year') || "",
cost: dataElement.getAttribute('data-cost') || "",
images: safeJsonParse(imagesData),
filter1: safeJsonParse(dataElement.getAttribute('data-filter1')),
filter2: safeJsonParse(dataElement.getAttribute('data-filter2')),
filter3: safeJsonParse(dataElement.getAttribute('data-filter3')),
original_id: dataElement.getAttribute('data-original-id') || ""
};
console.log("Server-provided duplicate data:", serverDuplicateData);
}
</script>
<style>
/* Book information display styles */
.book-info-container {
margin-top: 10px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #f9f9f9;
min-height: 40px;
}
.book-info {
padding: 15px;
background-color: var(--ui-surface);
border: 1px solid #ddd;
border-radius: 5px;
margin-bottom: 10px;
}
.book-info h4 {
color: var(--ui-text);
margin: 0 0 10px 0;
font-size: 1.2em;
font-weight: bold;
}
.book-info p {
margin: 5px 0;
color: #666;
line-height: 1.4;
}
.book-thumbnail {
max-width: 120px;
max-height: 180px;
float: right;
margin: 0 0 10px 15px;
border: 1px solid #ddd;
border-radius: 3px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.book-description {
clear: both;
margin-top: 15px;
padding-top: 10px;
border-top: 1px solid #eee;
}
.book-description h5 {
color: var(--ui-text);
margin: 0 0 8px 0;
font-size: 1em;
font-weight: bold;
}
.book-description p {
max-height: 150px;
overflow-y: auto;
padding: 5px;
background-color: var(--ui-surface-soft);
border: 1px solid #e9ecef;
border-radius: 3px;
font-size: 0.9em;
line-height: 1.5;
}
/* Import button styling */
.import-book-button {
background-color: #28a745;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
margin-top: 15px;
transition: background-color 0.2s ease;
display: block;
width: 100%;
}
.import-book-button:hover {
background-color: #218838;
}
.import-book-button:active {
background-color: #1e7e34;
}
/* Quagga Scanner Video Styles */
#code4-scanner video, #code4-scanner canvas,
#isbn-scanner video, #isbn-scanner canvas {
width: 100%;
max-width: 640px;
height: auto;
border-radius: 5px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
#code4-scanner canvas, #isbn-scanner canvas {
position: absolute;
top: 0;
left: 0;
}
#code4-scanner, #isbn-scanner {
position: relative;
overflow: hidden;
}
/* ISBN input group styling */
.isbn-input-group {
display: flex;
gap: 10px;
align-items: center;
}
.isbn-input-group input {
flex: 1;
}
.fetch-isbn-button {
background-color: #007bff;
color: white;
border: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
white-space: nowrap;
transition: background-color 0.2s ease;
}
.fetch-isbn-button:hover {
background-color: #0056b3;
}
/* Loading spinner */
.loading-spinner {
color: #007bff;
font-style: italic;
text-align: center;
padding: 20px;
background-color: var(--ui-surface-soft);
border: 1px solid #dee2e6;
border-radius: 4px;
}
.loading-spinner::after {
content: "...";
animation: dots 1.5s steps(5, end) infinite;
}
@keyframes dots {
0%, 20% {
color: rgba(0,0,0,0);
text-shadow: .25em 0 0 rgba(0,0,0,0), .5em 0 0 rgba(0,0,0,0);
}
40% {
color: #007bff;
text-shadow: .25em 0 0 rgba(0,0,0,0), .5em 0 0 rgba(0,0,0,0);
}
60% {
text-shadow: .25em 0 0 #007bff, .5em 0 0 rgba(0,0,0,0);
}
80%, 100% {
text-shadow: .25em 0 0 #007bff, .5em 0 0 #007bff;
}
}
/* Error message styling */
.error-message {
color: #721c24;
background-color: #f8d7da;
border: 1px solid #f5c6cb;
border-radius: 4px;
padding: 12px;
margin: 10px 0;
text-align: center;
font-weight: 500;
}
/* Success message styling */
.import-success-message {
color: #155724;
background-color: #d4edda;
border: 1px solid #c3e6cb;
border-radius: 4px;
padding: 12px;
margin: 10px 0;
text-align: center;
font-weight: bold;
animation: fadeInOut 3s ease;
}
@keyframes fadeInOut {
0% { opacity: 0; transform: translateY(-10px); }
10%, 90% { opacity: 1; transform: translateY(0); }
100% { opacity: 0; transform: translateY(-10px); }
}
/* Code validation styling */
.code-checking {
background-color: #fff3cd !important;
border-color: #ffc107 !important;
position: relative;
}
.code-checking::after {
content: "Prüfe...";
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
font-size: 0.8em;
color: #856404;
font-style: italic;
}
.code-valid {
background-color: #d4edda !important;
border-color: #28a745 !important;
color: #155724 !important;
}
.code-invalid {
background-color: #f8d7da !important;
border-color: #dc3545 !important;
color: #721c24 !important;
}
/* Duplicate code popup styling */
.popup-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.6);
z-index: 10000;
display: flex;
justify-content: center;
align-items: center;
animation: overlayFadeIn 0.3s ease;
}
@keyframes overlayFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.duplicate-code-popup {
background-color: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
max-width: 500px;
width: 90%;
text-align: center;
position: relative;
animation: popupSlideIn 0.3s ease;
}
@keyframes popupSlideIn {
from {
opacity: 0;
transform: scale(0.8) translateY(-20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.popup-content h3 {
color: #dc3545;
margin: 0 0 15px 0;
font-size: 1.3em;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}
.popup-content h3::before {
content: "⚠️";
font-size: 1.2em;
}
.popup-content p {
color: var(--ui-text);
margin: 15px 0;
line-height: 1.5;
font-size: 1em;
}
.popup-content .duplicate-code {
font-weight: bold;
color: #dc3545;
background-color: #f8d7da;
padding: 5px 10px;
border-radius: 3px;
font-family: monospace;
font-size: 1.1em;
}
.popup-close-button {
background-color: #6c757d;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
margin-top: 20px;
transition: background-color 0.2s ease;
}
.popup-close-button:hover {
background-color: #5a6268;
}
.popup-close-x {
position: absolute;
top: 10px;
right: 15px;
background: none;
border: none;
font-size: 24px;
color: #999;
cursor: pointer;
padding: 5px;
line-height: 1;
}
.popup-close-x:hover {
color: var(--ui-text);
}
/* Upload form styles */
.upload-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: var(--ui-surface);
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.upload-form h1 {
color: var(--ui-text);
margin-bottom: 30px;
text-align: center;
font-size: 2em;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: var(--ui-text);
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
box-sizing: border-box;
}
.form-group textarea {
height: 100px;
resize: vertical;
}
.filter-inputs {
background-color: var(--ui-surface-soft);
padding: 20px;
border-radius: 5px;
margin: 20px 0;
}
.filter-inputs h3 {
color: var(--ui-text);
margin-bottom: 15px;
font-size: 1.2em;
}
.multi-filter {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.filter-dropdown-select {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: white;
}
.add-new-btn {
background-color: #007bff;
color: white;
border: none;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
margin-top: 5px;
font-size: 12px;
}
.add-new-btn:hover {
background-color: #0056b3;
}
.image-preview-container {
margin-top: 10px;
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.image-preview {
position: relative;
display: inline-block;
}
.image-preview img {
width: 100px;
height: 100px;
object-fit: cover;
border: 1px solid #ddd;
border-radius: 4px;
}
.remove-image {
position: absolute;
top: -5px;
right: -5px;
background-color: #dc3545;
color: white;
border: none;
border-radius: 50%;
width: 20px;
height: 20px;
font-size: 12px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
/* Video preview styles */
.video-preview {
border: 2px dashed #17a2b8;
background-color: #f0f8ff;
}
.video-placeholder {
width: 100px;
height: 100px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #e9ecef;
border-radius: 4px;
text-align: center;
}
.video-icon {
font-size: 30px;
margin-bottom: 5px;
}
.video-name {
font-size: 10px;
word-break: break-all;
max-width: 90px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Book cover preview styles */
.book-cover-preview {
margin-top: 10px;
padding: 10px;
border: 2px dashed #007bff;
border-radius: 5px;
background-color: #f8f9ff;
}
.book-cover-preview .preview-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
}
.book-cover-preview img {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.image-loading {
padding: 10px;
text-align: center;
color: #666;
font-style: italic;
}
.submit-button {
background-color: #28a745;
color: white;
border: none;
padding: 15px 30px;
border-radius: 5px;
font-size: 18px;
font-weight: bold;
cursor: pointer;
width: 100%;
margin-top: 20px;
transition: background-color 0.2s ease;
}
.submit-button:hover {
background-color: #218838;
}
.submit-button:active {
background-color: #1e7e34;
}
/* Navigation button */
.nav-back-button {
background-color: #6c757d;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
text-decoration: none;
display: inline-block;
margin-bottom: 20px;
transition: background-color 0.2s ease;
}
.nav-back-button:hover {
background-color: #5a6268;
color: white;
text-decoration: none;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.upload-container {
margin: 10px;
padding: 15px;
}
.multi-filter {
grid-template-columns: 1fr;
}
.isbn-input-group {
flex-direction: column;
gap: 8px;
}
.fetch-isbn-button {
width: 100%;
}
.book-thumbnail {
float: none;
display: block;
margin: 10px auto;
}
}
@media (max-width: 480px) {
.upload-form h1 {
font-size: 1.5em;
}
.submit-button {
padding: 12px 20px;
font-size: 16px;
}
}
/* Additional styles for elements with inline styles */
.edit-new-location-container {
display: none;
margin-top: 10px;
}
.book-cover-thumbnail {
max-width: 100px;
max-height: 150px;
border: 1px solid #ddd;
border-radius: 3px;
}
/* Standardized button styles across the application */
.import-book-button, .fetch-isbn-button, .add-new-btn, .popup-close-button, .nav-back-button,
.submit-button, button[type="submit"], button[type="button"] {
display: inline-flex;
align-items: center;
justify-content: center;
text-align: center;
min-width: 120px;
height: 40px;
padding: 8px 16px;
font-weight: bold;
font-size: 0.9rem;
border-radius: 5px;
cursor: pointer;
white-space: nowrap;
box-sizing: border-box;
}
.book-cover-caption {
font-size: 0.8em;
color: #666;
margin: 5px 0;
}
.remove-book-cover-button {
background: #dc3545;
color: white;
border: none;
padding: 2px 8px;
border-radius: 3px;
cursor: pointer;
font-size: 0.8em;
}
.duplicate-image-preview {
max-width: 150px;
max-height: 150px;
object-fit: cover;
}
.remove-duplicate-image-button {
background: #dc3545;
color: white;
border: none;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
margin-left: 10px;
}
</style>
<div class="edit-container">
<div class="edit-form">
<h1>{{ page_title|default('Artikel bearbeiten') }}</h1>
<form id="edit-item-form" method="POST" action="{{ url_for('item_edit', id=item._id) }}" enctype="multipart/form-data">
<!-- Hidden IDs -->
<input type="hidden" name="item_id" value="{{ item._id }}">
<input type="hidden" name="series_group_id" value="{{ item.SeriesGroupId if item.SeriesGroupId else '' }}">
{% if show_library_features %}
<!-- Bibliotheks-Modus: ISBN & Barcode -->
<div class="form-group">
<label for="isbn">ISBN/Barcode:</label>
<div class="isbn-input-group" style="display:flex; gap:10px;">
<input type="text" id="isbn" name="isbn" placeholder="ISBN oder Barcode..." value="{{ item.ISBN if item.ISBN else '' }}" class="form-control">
<button type="button" id="scan-isbn-btn" class="btn btn-secondary">Barcode scannen</button>
<button type="button" class="btn btn-info" onclick="fetchBookInfo('edit')">Info abrufen</button>
</div>
<div id="isbn-scanner" style="width:100%; max-width:520px; display:none; margin-top:10px;"></div>
<small id="isbn-scan-status" class="form-text text-muted"></small>
<div id="book-info-container" class="book-info-container"></div>
</div>
{% endif %}
<!-- Name / Titel -->
<div class="form-group">
<label for="name">Name / Titel:</label>
<input type="text" id="name" name="name" value="{{ item.Name if item.Name else '' }}" class="form-control" required>
</div>
<!-- Ort -->
<div class="form-group">
<label for="ort">Ort / Standort:</label>
<select id="ort" name="ort" class="form-control" data-current-value="{{ item.Ort if item.Ort else '' }}" required>
<option value="">-- Bitte Ort auswählen --</option>
</select>
<button type="button" class="btn btn-sm btn-link" id="add-new-location-btn">Neuen Ort hinzufügen</button>
<div id="new-location-container" style="display:none; margin-top:5px;">
<input type="text" id="new-location-input" placeholder="Neuen Ort eingeben" class="form-control" style="display:inline-block; width:auto;">
<button type="button" class="btn btn-sm btn-success" onclick="addNewLocation()">Hinzufügen</button>
<button type="button" class="btn btn-sm btn-secondary" onclick="cancelAddLocation()">Abbrechen</button>
</div>
</div>
<!-- Beschreibung -->
<div class="form-group">
<label for="beschreibung">Beschreibung:</label>
<textarea id="beschreibung" name="beschreibung" rows="4" class="form-control" required>{{ item.Beschreibung if item.Beschreibung else '' }}</textarea>
</div>
<!-- Barcode / Code_4 -->
<div class="form-group" id="primary_code_group">
<label for="code_4">Barcode / Artikel-Code:</label>
<div style="display: flex; gap: 10px;">
<input type="text" id="code_4" name="code_4" class="form-control" value="{{ item.Code_4 if item.Code_4 else '' }}" required>
<button type="button" id="scan-code4-btn" class="btn btn-primary">Barcode scannen</button>
</div>
<div id="code4-scanner" style="display:none; margin-top: 10px;"></div>
<small id="code4-scan-status" class="form-text text-muted"></small>
</div>
{% if show_library_features %}
<!-- Bibliotheks-Klassifizierung -->
<div class="filter-inputs">
<h3>Medientyp</h3>
<div class="form-group">
{% set current_type = item.ItemType if item.ItemType else '' %}
<select name="item_type_input" id="item_type_input" class="form-control">
<option value="" disabled {% if not current_type %}selected{% endif %}>Medientyp auswählen...</option>
<option value="Buch" {% if current_type == 'Buch' %}selected{% endif %}>Buch</option>
<option value="Schulbuch" {% if current_type == 'Schulbuch' %}selected{% endif %}>Schulbuch</option>
<option value="CD" {% if current_type == 'CD' %}selected{% endif %}>CD</option>
<option value="DVD" {% if current_type == 'DVD' %}selected{% endif %}>DVD</option>
<option value="Sonstiges" {% if current_type == 'Sonstiges' %}selected{% endif %}>Sonstiges</option>
</select>
</div>
<h3>Kategorie / Fach:</h3>
<div class="form-group">
<input type="text" name="library_category" id="library_category" class="form-control" value="{{ item.Kategorie if item.Kategorie else '' }}" placeholder="z.B. Belletristik, Sachbücher...">
</div>
</div>
{% else %}
<!-- Inventar-Filter -->
<div class="filter-inputs">
<h3>Unterrichtsfach:</h3>
<div class="multi-filter">
{% for i in range(1, 5) %}
<div class="form-group">
<label for="filter1-{{ i }}">Wert {{ i }}:</label>
<select id="filter1-{{ i }}" name="filter" class="filter-dropdown-select form-control" data-current-value="{{ item.Filter1[i-1] if item.Filter1 and item.Filter1|length >= i else '' }}" {% if i == 1 %}required{% endif %}>
<option value="">-- {% if i == 1 %}Bitte auswählen{% else %}Optional{% endif %} --</option>
</select>
</div>
{% endfor %}
</div>
<h3>Jahrgangsstufe:</h3>
<div class="multi-filter">
{% for i in range(1, 5) %}
<div class="form-group">
<label for="filter2-{{ i }}">Wert {{ i }}:</label>
<select id="filter2-{{ i }}" name="filter2" class="filter-dropdown-select form-control" data-current-value="{{ item.Filter2[i-1] if item.Filter2 and item.Filter2|length >= i else '' }}" {% if i == 1 %}required{% endif %}>
<option value="">-- {% if i == 1 %}Bitte auswählen{% else %}Optional{% endif %} --</option>
</select>
</div>
{% endfor %}
</div>
<h3>Schlagwörter:</h3>
<div class="multi-filter">
{% for i in range(1, 5) %}
<div class="form-group">
<label for="filter3-{{ i }}">Wert {{ i }}:</label>
<input type="text" id="filter3-{{ i }}" name="filter3" class="form-control" value="{{ item.Filter3[i-1] if item.Filter3 and item.Filter3|length >= i else '' }}">
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Anschaffung & Kosten -->
<div class="form-group">
<label for="anschaffungsjahr">Anschaffungsjahr:</label>
<input type="date" id="anschaffungsjahr" name="anschaffungsjahr" class="form-control" value="{{ item.Anschaffungsjahr if item.Anschaffungsjahr else '' }}">
</div>
<div class="form-group">
<label for="anschaffungskosten">Anschaffungskosten (€):</label>
<input type="text" id="anschaffungskosten" name="anschaffungskosten" class="form-control" value="{{ item.Anschaffungskosten if item.Anschaffungskosten else '' }}">
</div>
<!-- Bilder verwalten -->
<div class="form-group">
<label>Bestehende Bilder:</label>
<div class="existing-images-container" style="display:flex; gap:10px; flex-wrap:wrap; margin-bottom:10px;">
{% if item.Images %}
{% for img in item.Images %}
<div class="image-keep-item" style="text-align:center;">
<img src="{{ url_for('uploaded_file', filename=img) }}" style="max-width:80px; max-height:80px; display:block; margin-bottom:4px;">
<label style="font-size:0.8em;"><input type="checkbox" name="existing_images" value="{{ img }}" checked> Behalten</label>
</div>
{% endfor %}
{% else %}
<small class="text-muted">Keine Bilder vorhanden.</small>
{% endif %}
</div>
<label for="images">Neue Bilder/Videos hinzufügen:</label>
<input type="file" id="images" name="images" accept=".jpg,.jpeg,.png,.gif,.mp4,.mov,.avi,.mkv,.webm" class="form-control" multiple>
<div class="image-preview-container" id="image-preview-container" style="display:flex; gap:10px; margin-top:10px;"></div>
</div>
<!-- Reservierbarkeit -->
<div class="form-group">
<label for="reservierbar" style="display:inline-block; width:auto; margin-right:10px;">Reservierbar:</label>
<input type="checkbox" id="reservierbar" name="reservierbar" style="width:auto;" {% if item.Reservierbar or item.Reservierbar is not defined %}checked{% endif %}>
</div>
<button type="submit" class="btn btn-primary submit-button">Änderungen speichern</button>
</form>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2/dist/quagga.js"></script>
<script>
const libraryModuleEnabled = {{ 'true' if library_module_enabled else 'false' }};
let scannerRunning = false;
let activeScannerCallback = null;
let code4LastScanned = '';
let code4LastScannedAt = 0;
let currentBookData = null;
function getItemId() {
const input = document.querySelector('input[name="item_id"]');
return input ? input.value : null;
}
function runEngineInitialization(targetSelector, activeCallback, completionMsg, errorStatusSetter) {
if (scannerRunning) { Quagga.stop(); scannerRunning = false; }
activeScannerCallback = activeCallback;
Quagga.init({
inputStream: { name: "Live", type: "LiveStream", target: document.querySelector(targetSelector), constraints: { width: 640, height: 480, facingMode: "environment" } },
decoder: { readers: ["code_128_reader", "ean_reader", "code_39_reader", "upc_reader"] }
}, function(err) {
if (err) { errorStatusSetter("Kamera-Fehler.", true); return; }
Quagga.start(); scannerRunning = true; errorStatusSetter(completionMsg, false);
});
}
function killScannerHardware() {
if (!scannerRunning) return;
Quagga.stop(); scannerRunning = false; activeScannerCallback = null;
}
Quagga.onDetected(function(data) {
if (!data || !data.codeResult || !data.codeResult.code) return;
if (typeof activeScannerCallback === "function") activeScannerCallback(String(data.codeResult.code).trim());
});
function startCode4Scanner() {
const scannerBox = document.getElementById('code4-scanner');
const scanButton = document.getElementById('scan-code4-btn');
const codeField = document.getElementById('code_4');
if (!scannerBox || !scanButton || !codeField) return;
if (scannerBox.style.display !== 'none') {
killScannerHardware(); scannerBox.style.display = 'none'; scanButton.textContent = 'Barcode scannen'; return;
}
scannerBox.style.display = 'block'; scanButton.textContent = 'Scanner stoppen';
runEngineInitialization('#code4-scanner', function(decodedText) {
const now = Date.now();
if (decodedText === code4LastScanned && (now - code4LastScannedAt) < 1500) return;
code4LastScanned = decodedText; code4LastScannedAt = now;
killScannerHardware(); scannerBox.style.display = 'none'; scanButton.textContent = 'Barcode scannen';
codeField.value = decodedText;
validateCodeField(codeField, getItemId());
}, 'Scanner läuft...', (msg) => { document.getElementById('code4-scan-status').textContent = msg; });
}
function validateCodeField(input, excludeId) {
const code = input.value.trim();
if (!code) return;
const currentId = excludeId || getItemId();
fetch(`/check_code_unique/${encodeURIComponent(code)}?exclude_id=${currentId}`)
.then(res => res.json())
.then(data => {
if (data.is_unique) {
input.classList.remove('is-invalid'); input.classList.add('is-valid');
} else {
input.classList.remove('is-valid'); input.classList.add('is-invalid');
alert(`Der Code "${code}" wird bereits verwendet!`);
}
});
}
function loadLocationOptions() {
fetch('/get_predefined_locations')
.then(res => res.json())
.then(data => {
const select = document.getElementById('ort');
if (!select) return;
const currentVal = select.dataset.currentValue || '';
while (select.children.length > 1) select.removeChild(select.lastChild);
data.locations.forEach(loc => {
const opt = document.createElement('option');
opt.value = loc; opt.textContent = loc;
if (currentVal && loc === currentVal) opt.selected = true;
select.appendChild(opt);
});
});
}
function addNewLocation() {
const input = document.getElementById('new-location-input');
if (!input.value.trim()) return;
const select = document.getElementById('ort');
const opt = document.createElement('option');
opt.value = input.value.trim(); opt.textContent = input.value.trim(); opt.selected = true;
select.appendChild(opt);
cancelAddLocation();
}
function cancelAddLocation() {
document.getElementById('new-location-container').style.display = 'none';
document.getElementById('new-location-input').value = '';
}
document.addEventListener('DOMContentLoaded', function() {
loadLocationOptions();
const scanCodeBtn = document.getElementById('scan-code4-btn');
if (scanCodeBtn) scanCodeBtn.addEventListener('click', startCode4Scanner);
const addLocBtn = document.getElementById('add-new-location-btn');
if (addLocBtn) addLocBtn.addEventListener('click', () => {
const container = document.getElementById('new-location-container');
container.style.display = container.style.display === 'none' ? 'block' : 'none';
});
const codeField = document.getElementById('code_4');
if (codeField) codeField.addEventListener('blur', function() { validateCodeField(this, getItemId()); });
});
</script>