Add bulk selection functionality for filter options in admin templates
This commit is contained in:
Binary file not shown.
+48
@@ -713,6 +713,42 @@ def sanitize_form_value(value):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
FILTER_SELECT_ALL_TOKEN = '__ALL__'
|
||||||
|
|
||||||
|
|
||||||
|
def expand_filter_selection(selected_values, filter_num):
|
||||||
|
"""Expand special filter token into all predefined values for a filter."""
|
||||||
|
if not isinstance(selected_values, list):
|
||||||
|
return []
|
||||||
|
|
||||||
|
has_select_all = False
|
||||||
|
cleaned_values = []
|
||||||
|
seen = set()
|
||||||
|
|
||||||
|
for raw_value in selected_values:
|
||||||
|
value = str(raw_value).strip() if raw_value is not None else ''
|
||||||
|
if not value:
|
||||||
|
continue
|
||||||
|
if value == FILTER_SELECT_ALL_TOKEN:
|
||||||
|
has_select_all = True
|
||||||
|
continue
|
||||||
|
if value not in seen:
|
||||||
|
cleaned_values.append(value)
|
||||||
|
seen.add(value)
|
||||||
|
|
||||||
|
if not has_select_all:
|
||||||
|
return cleaned_values
|
||||||
|
|
||||||
|
predefined_values = it.get_predefined_filter_values(filter_num)
|
||||||
|
for predefined_value in predefined_values:
|
||||||
|
value = str(predefined_value).strip() if predefined_value is not None else ''
|
||||||
|
if value and value not in seen:
|
||||||
|
cleaned_values.append(value)
|
||||||
|
seen.add(value)
|
||||||
|
|
||||||
|
return cleaned_values
|
||||||
|
|
||||||
|
|
||||||
def normalize_isbn(isbn_raw):
|
def normalize_isbn(isbn_raw):
|
||||||
"""Return only digits/X from ISBN input in canonical uppercase form."""
|
"""Return only digits/X from ISBN input in canonical uppercase form."""
|
||||||
if isbn_raw is None:
|
if isbn_raw is None:
|
||||||
@@ -2638,6 +2674,10 @@ def upload_item():
|
|||||||
flash('Fehler beim Verarbeiten der Formulardaten. Bitte versuchen Sie es erneut.', 'error')
|
flash('Fehler beim Verarbeiten der Formulardaten. Bitte versuchen Sie es erneut.', 'error')
|
||||||
return redirect(url_for('home_admin'))
|
return redirect(url_for('home_admin'))
|
||||||
|
|
||||||
|
# Expand special "all values" selections for predefined filters.
|
||||||
|
filter_upload = expand_filter_selection(filter_upload, 1)
|
||||||
|
filter_upload2 = expand_filter_selection(filter_upload2, 2)
|
||||||
|
|
||||||
# Validation
|
# Validation
|
||||||
if not name or not ort or not beschreibung:
|
if not name or not ort or not beschreibung:
|
||||||
error_msg = 'Bitte füllen Sie alle erforderlichen Felder aus'
|
error_msg = 'Bitte füllen Sie alle erforderlichen Felder aus'
|
||||||
@@ -3684,6 +3724,10 @@ def edit_item(id):
|
|||||||
filter2 = sanitize_form_value(request.form.getlist('filter2'))
|
filter2 = sanitize_form_value(request.form.getlist('filter2'))
|
||||||
filter3 = sanitize_form_value(request.form.getlist('filter3'))
|
filter3 = sanitize_form_value(request.form.getlist('filter3'))
|
||||||
|
|
||||||
|
# Expand special "all values" selections for predefined filters.
|
||||||
|
filter1 = expand_filter_selection(filter1, 1)
|
||||||
|
filter2 = expand_filter_selection(filter2, 2)
|
||||||
|
|
||||||
anschaffungs_jahr = sanitize_form_value(request.form.get('anschaffungsjahr'))
|
anschaffungs_jahr = sanitize_form_value(request.form.get('anschaffungsjahr'))
|
||||||
anschaffungs_kosten = sanitize_form_value(request.form.get('anschaffungskosten'))
|
anschaffungs_kosten = sanitize_form_value(request.form.get('anschaffungskosten'))
|
||||||
code_4 = sanitize_form_value(request.form.get('code_4'))
|
code_4 = sanitize_form_value(request.form.get('code_4'))
|
||||||
@@ -5907,6 +5951,10 @@ def add_filter_value(filter_num):
|
|||||||
flash('Bitte geben Sie einen Wert ein', 'error')
|
flash('Bitte geben Sie einen Wert ein', 'error')
|
||||||
return redirect(url_for('manage_filters'))
|
return redirect(url_for('manage_filters'))
|
||||||
|
|
||||||
|
if value == FILTER_SELECT_ALL_TOKEN:
|
||||||
|
flash('Dieser Wert ist reserviert und kann nicht als Filterwert gespeichert werden.', 'error')
|
||||||
|
return redirect(url_for('manage_filters'))
|
||||||
|
|
||||||
# Add the value to the filter
|
# Add the value to the filter
|
||||||
success = it.add_predefined_filter_value(filter_num, value)
|
success = it.add_predefined_filter_value(filter_num, value)
|
||||||
|
|
||||||
|
|||||||
@@ -1923,6 +1923,42 @@
|
|||||||
applyFilters();
|
applyFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bulkToggleFilterOptions(filterNum, shouldSelect) {
|
||||||
|
const filterKey = `filter${filterNum}`;
|
||||||
|
const checkboxes = Array.from(document.querySelectorAll(`#filter${filterNum}-options input[type="checkbox"]`));
|
||||||
|
if (checkboxes.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextValues = new Set(activeFilters[filterKey]);
|
||||||
|
|
||||||
|
checkboxes.forEach(checkbox => {
|
||||||
|
const value = checkbox.value;
|
||||||
|
const group = checkbox.dataset.group || '';
|
||||||
|
const filterValue = group ? `${group}:${value}` : value;
|
||||||
|
checkbox.checked = shouldSelect;
|
||||||
|
|
||||||
|
if (shouldSelect) {
|
||||||
|
nextValues.add(filterValue);
|
||||||
|
} else {
|
||||||
|
nextValues.delete(filterValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
activeFilters[filterKey] = Array.from(nextValues);
|
||||||
|
updateSelectedFilters(filterNum);
|
||||||
|
|
||||||
|
if (filterNum === 1) {
|
||||||
|
rebuildFilter2Options();
|
||||||
|
rebuildFilter3Options();
|
||||||
|
} else if (filterNum === 2) {
|
||||||
|
rebuildFilter3Options();
|
||||||
|
}
|
||||||
|
|
||||||
|
saveFilterState();
|
||||||
|
applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
function populateFilterOptions(containerId, filterValues, filterNum) {
|
function populateFilterOptions(containerId, filterValues, filterNum) {
|
||||||
const container = document.getElementById(containerId);
|
const container = document.getElementById(containerId);
|
||||||
if (!container) {
|
if (!container) {
|
||||||
@@ -1932,6 +1968,27 @@
|
|||||||
|
|
||||||
container.innerHTML = ''; // Clear previous options
|
container.innerHTML = ''; // Clear previous options
|
||||||
|
|
||||||
|
const bulkActions = document.createElement('div');
|
||||||
|
bulkActions.style.display = 'flex';
|
||||||
|
bulkActions.style.gap = '8px';
|
||||||
|
bulkActions.style.marginBottom = '10px';
|
||||||
|
|
||||||
|
const selectAllBtn = document.createElement('button');
|
||||||
|
selectAllBtn.type = 'button';
|
||||||
|
selectAllBtn.className = 'clear-filter';
|
||||||
|
selectAllBtn.textContent = 'Alle wählen';
|
||||||
|
selectAllBtn.onclick = () => bulkToggleFilterOptions(filterNum, true);
|
||||||
|
|
||||||
|
const clearAllBtn = document.createElement('button');
|
||||||
|
clearAllBtn.type = 'button';
|
||||||
|
clearAllBtn.className = 'clear-filter';
|
||||||
|
clearAllBtn.textContent = 'Alle abwählen';
|
||||||
|
clearAllBtn.onclick = () => bulkToggleFilterOptions(filterNum, false);
|
||||||
|
|
||||||
|
bulkActions.appendChild(selectAllBtn);
|
||||||
|
bulkActions.appendChild(clearAllBtn);
|
||||||
|
container.appendChild(bulkActions);
|
||||||
|
|
||||||
// First group values by category/prefix if they exist
|
// First group values by category/prefix if they exist
|
||||||
const groupedValues = {};
|
const groupedValues = {};
|
||||||
|
|
||||||
|
|||||||
@@ -2848,6 +2848,11 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
|||||||
select.removeChild(select.lastChild);
|
select.removeChild(select.lastChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const allOption = document.createElement('option');
|
||||||
|
allOption.value = '__ALL__';
|
||||||
|
allOption.textContent = '-- Alle auswählen --';
|
||||||
|
select.appendChild(allOption);
|
||||||
|
|
||||||
// Add new options - data.values contains the array
|
// Add new options - data.values contains the array
|
||||||
data.values.forEach(filter => {
|
data.values.forEach(filter => {
|
||||||
if (filter && filter.trim() !== '') {
|
if (filter && filter.trim() !== '') {
|
||||||
@@ -4401,6 +4406,42 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
|||||||
applyFilters();
|
applyFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bulkToggleFilterOptions(filterNum, shouldSelect) {
|
||||||
|
const filterKey = `filter${filterNum}`;
|
||||||
|
const checkboxes = Array.from(document.querySelectorAll(`#filter${filterNum}-options input[type="checkbox"]`));
|
||||||
|
if (checkboxes.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextValues = new Set(activeFilters[filterKey]);
|
||||||
|
|
||||||
|
checkboxes.forEach(checkbox => {
|
||||||
|
const value = checkbox.value;
|
||||||
|
const group = checkbox.dataset.group || '';
|
||||||
|
const filterValue = group ? `${group}:${value}` : value;
|
||||||
|
checkbox.checked = shouldSelect;
|
||||||
|
|
||||||
|
if (shouldSelect) {
|
||||||
|
nextValues.add(filterValue);
|
||||||
|
} else {
|
||||||
|
nextValues.delete(filterValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
activeFilters[filterKey] = Array.from(nextValues);
|
||||||
|
updateSelectedFiltersDisplay(filterNum);
|
||||||
|
|
||||||
|
if (filterNum === 1) {
|
||||||
|
rebuildFilter2Options();
|
||||||
|
rebuildFilter3Options();
|
||||||
|
} else if (filterNum === 2) {
|
||||||
|
rebuildFilter3Options();
|
||||||
|
}
|
||||||
|
|
||||||
|
saveFilterState();
|
||||||
|
applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
function populateFilterOptions(containerId, filterValues, filterNum) {
|
function populateFilterOptions(containerId, filterValues, filterNum) {
|
||||||
const container = document.getElementById(containerId);
|
const container = document.getElementById(containerId);
|
||||||
if (!container) {
|
if (!container) {
|
||||||
@@ -4409,6 +4450,27 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
|||||||
|
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
const bulkActions = document.createElement('div');
|
||||||
|
bulkActions.style.display = 'flex';
|
||||||
|
bulkActions.style.gap = '8px';
|
||||||
|
bulkActions.style.marginBottom = '10px';
|
||||||
|
|
||||||
|
const selectAllBtn = document.createElement('button');
|
||||||
|
selectAllBtn.type = 'button';
|
||||||
|
selectAllBtn.className = 'clear-filter';
|
||||||
|
selectAllBtn.textContent = 'Alle wählen';
|
||||||
|
selectAllBtn.onclick = () => bulkToggleFilterOptions(filterNum, true);
|
||||||
|
|
||||||
|
const clearAllBtn = document.createElement('button');
|
||||||
|
clearAllBtn.type = 'button';
|
||||||
|
clearAllBtn.className = 'clear-filter';
|
||||||
|
clearAllBtn.textContent = 'Alle abwählen';
|
||||||
|
clearAllBtn.onclick = () => bulkToggleFilterOptions(filterNum, false);
|
||||||
|
|
||||||
|
bulkActions.appendChild(selectAllBtn);
|
||||||
|
bulkActions.appendChild(clearAllBtn);
|
||||||
|
container.appendChild(bulkActions);
|
||||||
|
|
||||||
const groupedValues = {};
|
const groupedValues = {};
|
||||||
|
|
||||||
filterValues.forEach(value => {
|
filterValues.forEach(value => {
|
||||||
|
|||||||
@@ -1115,8 +1115,16 @@
|
|||||||
select.removeChild(select.lastChild);
|
select.removeChild(select.lastChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const allOption = document.createElement('option');
|
||||||
|
allOption.value = '__ALL__';
|
||||||
|
allOption.textContent = '-- Alle auswählen --';
|
||||||
|
select.appendChild(allOption);
|
||||||
|
|
||||||
// Add new options - data.values contains the array
|
// Add new options - data.values contains the array
|
||||||
data.values.forEach(value => {
|
data.values.forEach(value => {
|
||||||
|
if (!value || String(value).trim() === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
option.value = value;
|
option.value = value;
|
||||||
option.textContent = value;
|
option.textContent = value;
|
||||||
|
|||||||
Reference in New Issue
Block a user