feat: implement customizable filter names and update management interfaces
This commit is contained in:
+17
@@ -8589,11 +8589,15 @@ def manage_filters():
|
|||||||
filter2_values = it.get_predefined_filter_values(2)
|
filter2_values = it.get_predefined_filter_values(2)
|
||||||
filter3_values = it.get_predefined_filter_values(3)
|
filter3_values = it.get_predefined_filter_values(3)
|
||||||
|
|
||||||
|
# Get custom filter names
|
||||||
|
filter_names = it.get_filter_names()
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'manage_filters.html',
|
'manage_filters.html',
|
||||||
filter1_values=filter1_values,
|
filter1_values=filter1_values,
|
||||||
filter2_values=filter2_values,
|
filter2_values=filter2_values,
|
||||||
filter3_values=filter3_values,
|
filter3_values=filter3_values,
|
||||||
|
filter_names=filter_names,
|
||||||
library_module_enabled=cfg.MODULES.is_enabled('library'),
|
library_module_enabled=cfg.MODULES.is_enabled('library'),
|
||||||
student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards')
|
student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards')
|
||||||
)
|
)
|
||||||
@@ -9658,14 +9662,27 @@ def admin_school_settings():
|
|||||||
try:
|
try:
|
||||||
updated_school = cfg.update_school_info(school_info)
|
updated_school = cfg.update_school_info(school_info)
|
||||||
current_school = updated_school
|
current_school = updated_school
|
||||||
|
|
||||||
|
# Also process filter names
|
||||||
|
filter_name_1 = request.form.get('filter_name_1')
|
||||||
|
if filter_name_1: it.set_filter_name(1, filter_name_1.strip())
|
||||||
|
filter_name_2 = request.form.get('filter_name_2')
|
||||||
|
if filter_name_2: it.set_filter_name(2, filter_name_2.strip())
|
||||||
|
filter_name_3 = request.form.get('filter_name_3')
|
||||||
|
if filter_name_3: it.set_filter_name(3, filter_name_3.strip())
|
||||||
|
|
||||||
flash('Schulstammdaten wurden erfolgreich gespeichert.', 'success')
|
flash('Schulstammdaten wurden erfolgreich gespeichert.', 'success')
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
app.logger.error(f'Could not update school settings: {exc}\n{traceback.format_exc()}')
|
app.logger.error(f'Could not update school settings: {exc}\n{traceback.format_exc()}')
|
||||||
flash('Die Schulstammdaten konnten nicht gespeichert werden.', 'error')
|
flash('Die Schulstammdaten konnten nicht gespeichert werden.', 'error')
|
||||||
|
|
||||||
|
# Get current filter names to display in form
|
||||||
|
filter_names = it.get_filter_names()
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'admin_school_settings.html',
|
'admin_school_settings.html',
|
||||||
school_info=current_school,
|
school_info=current_school,
|
||||||
|
filter_names=filter_names,
|
||||||
tenant_id=tenant_id,
|
tenant_id=tenant_id,
|
||||||
tenant_db=tenant_db,
|
tenant_db=tenant_db,
|
||||||
library_module_enabled=cfg.MODULES.is_enabled('library'),
|
library_module_enabled=cfg.MODULES.is_enabled('library'),
|
||||||
|
|||||||
@@ -839,6 +839,34 @@ def edit_predefined_filter_value(filter_num, old_value, new_value):
|
|||||||
client.close()
|
client.close()
|
||||||
return result.modified_count > 0
|
return result.modified_count > 0
|
||||||
|
|
||||||
|
def get_filter_names():
|
||||||
|
"""Get customized filter category names."""
|
||||||
|
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||||
|
db = client[cfg.MONGODB_DB]
|
||||||
|
names_doc = db.settings.find_one({'setting_type': 'filter_names'})
|
||||||
|
client.close()
|
||||||
|
if names_doc and 'names' in names_doc:
|
||||||
|
return names_doc['names']
|
||||||
|
return {
|
||||||
|
'1': 'Fach/Kategorie',
|
||||||
|
'2': 'System/Bereich',
|
||||||
|
'3': 'Typ/Art'
|
||||||
|
}
|
||||||
|
|
||||||
|
def set_filter_name(filter_num, name):
|
||||||
|
"""Set custom name for a filter category."""
|
||||||
|
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||||
|
db = client[cfg.MONGODB_DB]
|
||||||
|
names = get_filter_names()
|
||||||
|
names[str(filter_num)] = name
|
||||||
|
db.settings.update_one(
|
||||||
|
{'setting_type': 'filter_names'},
|
||||||
|
{'$set': {'names': names}},
|
||||||
|
upsert=True
|
||||||
|
)
|
||||||
|
client.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
# === LOCATION MANAGEMENT ===
|
# === LOCATION MANAGEMENT ===
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,25 @@
|
|||||||
<small>Upload ersetzt das bisherige Logo. Wenn kein neues Logo gewählt wird, bleibt das aktuelle erhalten.</small>
|
<small>Upload ersetzt das bisherige Logo. Wenn kein neues Logo gewählt wird, bleibt das aktuelle erhalten.</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<hr style="margin: 24px 0; border: 0; border-top: 1px solid #e5e7eb;">
|
||||||
|
<h3 style="font-size: 1.1rem; margin-bottom: 16px; color: #111827;">Globales Filter-Wording</h3>
|
||||||
|
<p style="font-size: 0.9rem; color: #6b7280; margin-bottom: 16px;">Hier kannst du die globalen Bezeichnungen für die drei Haupt-Kategorien (Filter) der Anwendung ändern.</p>
|
||||||
|
|
||||||
|
<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">
|
||||||
|
</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">
|
||||||
|
</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">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="actions-row">
|
<div class="actions-row">
|
||||||
<button type="submit" class="btn btn-primary">Schulstammdaten speichern</button>
|
<button type="submit" class="btn btn-primary">Schulstammdaten speichern</button>
|
||||||
<a href="{{ url_for('home_admin') }}" class="btn btn-secondary">Zurück zur Admin-Übersicht</a>
|
<a href="{{ url_for('home_admin') }}" class="btn btn-secondary">Zurück zur Admin-Übersicht</a>
|
||||||
|
|||||||
@@ -329,7 +329,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* iPad/Tablet responsive adjustments */
|
/* iPad/Tablet responsive adjustments */
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 768px) {
|
||||||
.navbar .container-fluid {
|
.navbar .container-fluid {
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
padding-left: max(env(safe-area-inset-left, 0), 10px);
|
padding-left: max(env(safe-area-inset-left, 0), 10px);
|
||||||
|
|||||||
@@ -760,11 +760,14 @@
|
|||||||
if (!mainItemsLoadingIndicator) {
|
if (!mainItemsLoadingIndicator) {
|
||||||
mainItemsLoadingIndicator = document.createElement('div');
|
mainItemsLoadingIndicator = document.createElement('div');
|
||||||
mainItemsLoadingIndicator.id = 'main-items-loading-indicator';
|
mainItemsLoadingIndicator.id = 'main-items-loading-indicator';
|
||||||
mainItemsLoadingIndicator.className = 'items-loading-indicator';
|
mainItemsLoadingIndicator.className = 'items-loading-indicator empty-frames-placeholder';
|
||||||
mainItemsLoadingIndicator.innerHTML = `
|
mainItemsLoadingIndicator.innerHTML = `
|
||||||
<div class="loading-track"><div class="loading-fill"></div></div>
|
<div class="empty-frame p-3 mx-2" style="flex: 1; height: 100px; border-radius: 12px; background: rgba(255,255,255,0.02); min-width: 250px;"></div>
|
||||||
<div class="loading-label">Weitere Objekte werden vorbereitet...</div>
|
<div class="empty-frame p-3 mx-2" style="flex: 1; height: 100px; border-radius: 12px; background: rgba(255,255,255,0.02); min-width: 250px; display: none;" class="d-none d-md-block"></div>
|
||||||
|
<div class="empty-frame p-3 mx-2" style="flex: 1; height: 100px; border-radius: 12px; background: rgba(255,255,255,0.02); min-width: 250px; display: none;" class="d-none d-lg-block"></div>
|
||||||
`;
|
`;
|
||||||
|
mainItemsLoadingIndicator.style.display = 'flex';
|
||||||
|
mainItemsLoadingIndicator.style.width = '100%';
|
||||||
}
|
}
|
||||||
|
|
||||||
itemsContainer.appendChild(mainItemsLoadingIndicator);
|
itemsContainer.appendChild(mainItemsLoadingIndicator);
|
||||||
@@ -1200,6 +1203,7 @@
|
|||||||
card.innerHTML = card.dataset.savedHtml;
|
card.innerHTML = card.dataset.savedHtml;
|
||||||
card.dataset.deloaded = 'false';
|
card.dataset.deloaded = 'false';
|
||||||
card.classList.remove('is-deloaded');
|
card.classList.remove('is-deloaded');
|
||||||
|
card.classList.remove('empty-frame');
|
||||||
const favBtn = card.querySelector('.bookmark-btn');
|
const favBtn = card.querySelector('.bookmark-btn');
|
||||||
if (favBtn) {
|
if (favBtn) {
|
||||||
favBtn.addEventListener('click', (ev) => {
|
favBtn.addEventListener('click', (ev) => {
|
||||||
@@ -1227,12 +1231,13 @@
|
|||||||
card.innerHTML = '';
|
card.innerHTML = '';
|
||||||
card.dataset.deloaded = 'true';
|
card.dataset.deloaded = 'true';
|
||||||
card.classList.add('is-deloaded');
|
card.classList.add('is-deloaded');
|
||||||
|
card.classList.add('empty-frame');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, {
|
}, {
|
||||||
rootMargin: isMobileLayout ? '1500px 0px 1500px 0px' : '0px 2500px 0px 2500px'
|
rootMargin: '30000px 30000px 30000px 30000px'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3383,11 +3383,14 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
|||||||
if (!mainAdminItemsLoadingIndicator) {
|
if (!mainAdminItemsLoadingIndicator) {
|
||||||
mainAdminItemsLoadingIndicator = document.createElement('div');
|
mainAdminItemsLoadingIndicator = document.createElement('div');
|
||||||
mainAdminItemsLoadingIndicator.id = 'main-admin-items-loading-indicator';
|
mainAdminItemsLoadingIndicator.id = 'main-admin-items-loading-indicator';
|
||||||
mainAdminItemsLoadingIndicator.className = 'items-loading-indicator';
|
mainAdminItemsLoadingIndicator.className = 'items-loading-indicator empty-frames-placeholder';
|
||||||
mainAdminItemsLoadingIndicator.innerHTML = `
|
mainAdminItemsLoadingIndicator.innerHTML = `
|
||||||
<div class="loading-track"><div class="loading-fill"></div></div>
|
<div class="empty-frame p-3 mx-2" style="flex: 1; height: 100px; border-radius: 12px; background: rgba(255,255,255,0.02); min-width: 250px;"></div>
|
||||||
<div class="loading-label">Weitere Objekte werden vorbereitet...</div>
|
<div class="empty-frame p-3 mx-2" style="flex: 1; height: 100px; border-radius: 12px; background: rgba(255,255,255,0.02); min-width: 250px; display: none;" class="d-none d-md-block"></div>
|
||||||
|
<div class="empty-frame p-3 mx-2" style="flex: 1; height: 100px; border-radius: 12px; background: rgba(255,255,255,0.02); min-width: 250px; display: none;" class="d-none d-lg-block"></div>
|
||||||
`;
|
`;
|
||||||
|
mainAdminItemsLoadingIndicator.style.display = 'flex';
|
||||||
|
mainAdminItemsLoadingIndicator.style.width = '100%';
|
||||||
}
|
}
|
||||||
|
|
||||||
itemsContainer.appendChild(mainAdminItemsLoadingIndicator);
|
itemsContainer.appendChild(mainAdminItemsLoadingIndicator);
|
||||||
@@ -3812,6 +3815,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
|||||||
card.innerHTML = card.dataset.savedHtml;
|
card.innerHTML = card.dataset.savedHtml;
|
||||||
card.dataset.deloaded = 'false';
|
card.dataset.deloaded = 'false';
|
||||||
card.classList.remove('is-deloaded');
|
card.classList.remove('is-deloaded');
|
||||||
|
card.classList.remove('empty-frame');
|
||||||
const favBtn = card.querySelector('.bookmark-btn');
|
const favBtn = card.querySelector('.bookmark-btn');
|
||||||
if (favBtn) {
|
if (favBtn) {
|
||||||
favBtn.addEventListener('click', (ev) => {
|
favBtn.addEventListener('click', (ev) => {
|
||||||
@@ -3840,12 +3844,13 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
|||||||
card.innerHTML = '';
|
card.innerHTML = '';
|
||||||
card.dataset.deloaded = 'true';
|
card.dataset.deloaded = 'true';
|
||||||
card.classList.add('is-deloaded');
|
card.classList.add('is-deloaded');
|
||||||
|
card.classList.add('empty-frame');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, {
|
}, {
|
||||||
rootMargin: isMobileLayout ? '1500px 0px 1500px 0px' : '0px 2500px 0px 2500px'
|
rootMargin: '30000px 30000px 30000px 30000px'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h2 class="card-title h5 mb-0">Fach/Kategorie (Filter 1)</h2>
|
<h2 class="card-title h5 mb-0">{{ filter_names.get('1', 'Fach/Kategorie') }} (Filter 1)</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form method="POST" action="{{ url_for('add_filter_value', filter_num=1) }}" class="mb-4">
|
<form method="POST" action="{{ url_for('add_filter_value', filter_num=1) }}" class="mb-4">
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h2 class="card-title h5 mb-0">System/Bereich (Filter 2)</h2>
|
<h2 class="card-title h5 mb-0">{{ filter_names.get('2', 'System/Bereich') }} (Filter 2)</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form method="POST" action="{{ url_for('add_filter_value', filter_num=2) }}" class="mb-4">
|
<form method="POST" action="{{ url_for('add_filter_value', filter_num=2) }}" class="mb-4">
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h2 class="card-title h5 mb-0">Typ/Art (Filter 3)</h2>
|
<h2 class="card-title h5 mb-0">{{ filter_names.get('3', 'Typ/Art') }} (Filter 3)</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form method="POST" action="{{ url_for('add_filter_value', filter_num=3) }}" class="mb-4">
|
<form method="POST" action="{{ url_for('add_filter_value', filter_num=3) }}" class="mb-4">
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
from pymongo import MongoClient
|
||||||
|
import Web.settings as cfg
|
||||||
|
|
||||||
|
def get_filter_names():
|
||||||
|
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||||
|
db = client[cfg.MONGODB_DB]
|
||||||
|
names = db.settings.find_one({'setting_type': 'filter_names'})
|
||||||
|
client.close()
|
||||||
|
if names:
|
||||||
|
return names.get('names', {
|
||||||
|
'1': 'Fach/Kategorie',
|
||||||
|
'2': 'System/Bereich',
|
||||||
|
'3': 'Typ/Art'
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
'1': 'Fach/Kategorie',
|
||||||
|
'2': 'System/Bereich',
|
||||||
|
'3': 'Typ/Art'
|
||||||
|
}
|
||||||
|
|
||||||
|
def set_filter_name(filter_num, name):
|
||||||
|
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||||
|
db = client[cfg.MONGODB_DB]
|
||||||
|
names = get_filter_names()
|
||||||
|
names[str(filter_num)] = name
|
||||||
|
db.settings.update_one(
|
||||||
|
{'setting_type': 'filter_names'},
|
||||||
|
{'$set': {'names': names}},
|
||||||
|
upsert=True
|
||||||
|
)
|
||||||
|
client.close()
|
||||||
|
return True
|
||||||
Reference in New Issue
Block a user