Compare commits

...

4 Commits

Author SHA1 Message Date
Aiirondev_dev 4449007a15 Default Filternames Changes for the main_admin.html
Release Inventarsystem / release-docker (push) Successful in 2m14s
2026-08-24 16:08:24 +02:00
Aiirondev_dev 6770d42f99 Default Filternames Changed
Release Inventarsystem / release-docker (push) Successful in 2m15s
2026-08-24 16:00:58 +02:00
Aiirondev_dev 333f9fc89a New Modal Overlay
Release Inventarsystem / release-docker (push) Successful in 2m18s
2026-08-24 15:45:08 +02:00
Aiirondev_dev a286236834 New Modal Overlay
Release Inventarsystem / release-docker (push) Successful in 2m17s
2026-08-24 15:41:33 +02:00
5 changed files with 221 additions and 17 deletions
+3
View File
@@ -3203,6 +3203,8 @@ def home_admin():
flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error')
return redirect(url_for('library_view'))
filter_names = it.get_filter_names()
return render_template(
'main_admin.html',
username=session['username'],
@@ -3212,6 +3214,7 @@ def home_admin():
student_default_borrow_days=cfg.STUDENT_DEFAULT_BORROW_DAYS,
student_max_borrow_days=cfg.STUDENT_MAX_BORROW_DAYS,
school_info=_get_school_info_for_export(),
filter_names=filter_names,
open_item=request.args.get('open_item')
)
+3 -3
View File
@@ -911,9 +911,9 @@ def get_filter_names():
if names_doc and 'names' in names_doc:
return names_doc['names']
return {
'1': 'Fach/Kategorie',
'2': 'System/Bereich',
'3': 'Typ/Art'
'1': 'Klassenstufe',
'2': 'Fach',
'3': 'Schlagwort'
}
def set_filter_name(filter_num, name):
+3 -3
View File
@@ -78,15 +78,15 @@
<div class="form-row">
<div class="form-group">
<label for="filter_name_1">Name Filter 1</label>
<input type="text" id="filter_name_1" name="filter_name_1" value="{{ filter_names.get('1', 'Fach/Kategorie') }}" placeholder="z. B. Fach/Kategorie">
<input type="text" id="filter_name_1" name="filter_name_1" value="{{ filter_names.get('1', 'Klassenstufe') }}" placeholder="z. B. Klassenstufe">
</div>
<div class="form-group">
<label for="filter_name_2">Name Filter 2</label>
<input type="text" id="filter_name_2" name="filter_name_2" value="{{ filter_names.get('2', 'System/Bereich') }}" placeholder="z. B. System/Bereich">
<input type="text" id="filter_name_2" name="filter_name_2" value="{{ filter_names.get('2', 'Fach') }}" placeholder="z. B. Fach">
</div>
<div class="form-group">
<label for="filter_name_3">Name Filter 3</label>
<input type="text" id="filter_name_3" name="filter_name_3" value="{{ filter_names.get('3', 'Typ/Art') }}" placeholder="z. B. Typ/Art">
<input type="text" id="filter_name_3" name="filter_name_3" value="{{ filter_names.get('3', 'Schlagwort') }}" placeholder="z. B. Schlagwort">
</div>
</div>
+209 -8
View File
@@ -614,6 +614,199 @@
</div>
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2/dist/quagga.js"></script>
<script>
// =========================================================================
// CUSTOM MODAL HELPERS (Replaces native browser alert, confirm & prompt)
// =========================================================================
function createModalOverlay() {
const overlay = document.createElement('div');
overlay.style.position = 'fixed';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.width = '100vw';
overlay.style.height = '100vh';
overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';
overlay.style.display = 'flex';
overlay.style.alignItems = 'center';
overlay.style.justifyContent = 'center';
overlay.style.zIndex = '999999';
return overlay;
}
function createModalBox() {
const box = document.createElement('div');
box.style.backgroundColor = '#fff';
box.style.padding = '24px';
box.style.borderRadius = '8px';
box.style.boxShadow = '0 10px 25px rgba(0,0,0,0.2)';
box.style.fontFamily = 'sans-serif';
box.style.minWidth = '320px';
box.style.maxWidth = '90%';
box.style.textAlign = 'left';
box.style.color = '#333';
return box;
}
function customAlert(message) {
return new Promise((resolve) => {
const overlay = createModalOverlay();
const box = createModalBox();
const text = document.createElement('p');
text.textContent = message;
text.style.marginBottom = '20px';
text.style.lineHeight = '1.5';
text.style.whiteSpace = 'pre-wrap';
const btnContainer = document.createElement('div');
btnContainer.style.display = 'flex';
btnContainer.style.justifyContent = 'flex-end';
const btn = document.createElement('button');
btn.textContent = 'OK';
btn.style.padding = '8px 16px';
btn.style.cursor = 'pointer';
btn.style.border = 'none';
btn.style.backgroundColor = '#007bff';
btn.style.color = '#fff';
btn.style.borderRadius = '4px';
btn.onclick = () => {
document.body.removeChild(overlay);
resolve();
};
btnContainer.appendChild(btn);
box.appendChild(text);
box.appendChild(btnContainer);
overlay.appendChild(box);
document.body.appendChild(overlay);
btn.focus();
});
}
function customConfirm(message) {
return new Promise((resolve) => {
const overlay = createModalOverlay();
const box = createModalBox();
const text = document.createElement('p');
text.textContent = message;
text.style.marginBottom = '20px';
text.style.lineHeight = '1.5';
text.style.whiteSpace = 'pre-wrap';
const btnContainer = document.createElement('div');
btnContainer.style.display = 'flex';
btnContainer.style.justifyContent = 'flex-end';
btnContainer.style.gap = '10px';
const cancelBtn = document.createElement('button');
cancelBtn.textContent = 'Abbrechen';
cancelBtn.style.padding = '8px 16px';
cancelBtn.style.cursor = 'pointer';
cancelBtn.style.border = '1px solid #ccc';
cancelBtn.style.backgroundColor = '#f8f9fa';
cancelBtn.style.color = '#333';
cancelBtn.style.borderRadius = '4px';
const okBtn = document.createElement('button');
okBtn.textContent = 'OK';
okBtn.style.padding = '8px 16px';
okBtn.style.cursor = 'pointer';
okBtn.style.border = 'none';
okBtn.style.backgroundColor = '#007bff';
okBtn.style.color = '#fff';
okBtn.style.borderRadius = '4px';
const closeAndResolve = (val) => {
document.body.removeChild(overlay);
resolve(val);
};
cancelBtn.onclick = () => closeAndResolve(false);
okBtn.onclick = () => closeAndResolve(true);
btnContainer.appendChild(cancelBtn);
btnContainer.appendChild(okBtn);
box.appendChild(text);
box.appendChild(btnContainer);
overlay.appendChild(box);
document.body.appendChild(overlay);
okBtn.focus();
});
}
function customPrompt(message, defaultValue = '') {
return new Promise((resolve) => {
const overlay = createModalOverlay();
const box = createModalBox();
const text = document.createElement('p');
text.textContent = message;
text.style.marginBottom = '15px';
text.style.lineHeight = '1.5';
text.style.whiteSpace = 'pre-wrap';
const input = document.createElement('input');
input.type = 'text';
input.value = defaultValue;
input.style.width = '100%';
input.style.marginBottom = '20px';
input.style.padding = '10px';
input.style.boxSizing = 'border-box';
input.style.border = '1px solid #ccc';
input.style.borderRadius = '4px';
const btnContainer = document.createElement('div');
btnContainer.style.display = 'flex';
btnContainer.style.justifyContent = 'flex-end';
btnContainer.style.gap = '10px';
const cancelBtn = document.createElement('button');
cancelBtn.textContent = 'Abbrechen';
cancelBtn.style.padding = '8px 16px';
cancelBtn.style.cursor = 'pointer';
cancelBtn.style.border = '1px solid #ccc';
cancelBtn.style.backgroundColor = '#f8f9fa';
cancelBtn.style.color = '#333';
cancelBtn.style.borderRadius = '4px';
const okBtn = document.createElement('button');
okBtn.textContent = 'OK';
okBtn.style.padding = '8px 16px';
okBtn.style.cursor = 'pointer';
okBtn.style.border = 'none';
okBtn.style.backgroundColor = '#007bff';
okBtn.style.color = '#fff';
okBtn.style.borderRadius = '4px';
const closeAndResolve = (val) => {
document.body.removeChild(overlay);
resolve(val);
};
cancelBtn.onclick = () => closeAndResolve(null);
okBtn.onclick = () => closeAndResolve(input.value);
input.onkeydown = (e) => {
if (e.key === 'Enter') closeAndResolve(input.value);
if (e.key === 'Escape') closeAndResolve(null);
};
btnContainer.appendChild(cancelBtn);
btnContainer.appendChild(okBtn);
box.appendChild(text);
box.appendChild(input);
box.appendChild(btnContainer);
overlay.appendChild(box);
document.body.appendChild(overlay);
input.focus();
input.select();
});
}
// =========================================================================
// 1. GLOBAL STATE DEFINITIONS
// =========================================================================
@@ -1243,31 +1436,39 @@
// =========================================================================
// 4. UI INTERACTIONS & UTILITIES
// =========================================================================
function borrowItem(itemId) {
async 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.');
await customAlert('Dieses Medium ist als defekt/zerstört markiert und kann nicht ausgeliehen werden.');
return;
}
const defaultCardId = activeStudentCardId || '';
const cardId = (window.prompt('Bitte Bibliotheksausweis-ID eingeben:', defaultCardId) || '').trim().toUpperCase();
const promptCardResult = await customPrompt('Bitte Bibliotheksausweis-ID eingeben:', defaultCardId);
if (promptCardResult === null) return;
const cardId = promptCardResult.trim().toUpperCase();
if (!cardId) {
alert('Ausleihe abgebrochen: Für Bibliotheksmedien ist eine gültige Bibliotheksausweis-ID erforderlich.');
await customAlert('Ausleihe abgebrochen: Für Bibliotheksmedien ist eine gültige Bibliotheksausweis-ID erforderlich.');
return;
}
setActiveStudentCard(cardId);
const durationInput = (window.prompt('Ausleihdauer in Tagen (optional):') || '').trim();
const promptDurationResult = await customPrompt('Ausleihdauer in Tagen (optional):');
if (promptDurationResult === null) return;
const durationInput = promptDurationResult.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);
const countPromptResult = await customPrompt(`Anzahl ausleihen? (Standard: 1, verfügbar: ${maxAvailable})`, '1');
if (countPromptResult === null) return;
let borrowCount = parseInt(countPromptResult.trim() || '1', 10);
if (!Number.isFinite(borrowCount) || borrowCount < 1) {
borrowCount = 1;
}
if (borrowCount > maxAvailable) {
alert(`Es sind nur ${maxAvailable} Exemplar(e) verfügbar.`);
await customAlert(`Es sind nur ${maxAvailable} Exemplar(e) verfügbar.`);
return;
}
+3 -3
View File
@@ -2343,7 +2343,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
<div class="filter-container">
<div class="filter-group">
<div class="filter-header">
<label>Unterrichtsfach:</label>
<label>{{ filter_names.get('1', 'Klassenstufe') }}</label>
<button type="button" class="filter-toggle" onclick="toggleFilterDropdown('filter1-dropdown')"></button>
<button type="button" class="clear-filter" onclick="clearFilter(1)">Clear</button>
</div>
@@ -2357,7 +2357,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
<div class="filter-group">
<div class="filter-header">
<label>Jahrgangsstufe:</label>
<label>{{ filter_names.get('2', 'Fach') }}</label>
<button type="button" class="filter-toggle" onclick="toggleFilterDropdown('filter2-dropdown')"></button>
<button type="button" class="clear-filter" onclick="clearFilter(2)">Clear</button>
</div>
@@ -2371,7 +2371,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
<div class="filter-group">
<div class="filter-header">
<label>Schlagwort:</label>
<label>{{ filter_names.get('3', 'Schlagwort') }}</label>
<button type="button" class="filter-toggle" onclick="toggleFilterDropdown('filter3-dropdown')"></button>
<button type="button" class="clear-filter" onclick="clearFilter(3)">Clear</button>
</div>