feat(filter): added not equal to filter on sets page, so it is possible to filter for not-tags, not-status, not-year etc

This commit is contained in:
2025-12-25 14:35:59 -05:00
parent 4336ad4de3
commit f54dd3ec73
9 changed files with 323 additions and 65 deletions
+9 -1
View File
@@ -92,7 +92,15 @@ class BrickSetList(BrickRecordList[BrickSet]):
# Convert theme name to theme ID for filtering
theme_id_filter = None
if theme_filter:
theme_id_filter = self._theme_name_to_id(theme_filter)
# Check if this is a NOT filter
if theme_filter.startswith('-'):
# Extract the actual theme value without the "-" prefix
actual_theme = theme_filter[1:]
theme_id = self._theme_name_to_id(actual_theme)
# Re-add the "-" prefix to the theme ID
theme_id_filter = f'-{theme_id}' if theme_id else None
else:
theme_id_filter = self._theme_name_to_id(theme_filter)
# Check if any filters are applied
has_filters = any([status_filter, theme_id_filter, owner_filter, purchase_location_filter, storage_filter, tag_filter, year_filter, duplicate_filter])
+30 -2
View File
@@ -8,20 +8,36 @@ AND (LOWER("rebrickable_sets"."name") LIKE LOWER('%{{ search_query }}%')
{% endif %}
{% if theme_filter %}
{% if theme_filter is string and theme_filter.startswith('-') %}
AND "rebrickable_sets"."theme_id" != {{ theme_filter[1:] }}
{% else %}
AND "rebrickable_sets"."theme_id" = {{ theme_filter }}
{% endif %}
{% endif %}
{% if year_filter %}
{% if year_filter is string and year_filter.startswith('-') %}
AND "rebrickable_sets"."year" != {{ year_filter[1:] }}
{% else %}
AND "rebrickable_sets"."year" = {{ year_filter }}
{% endif %}
{% endif %}
{% if storage_filter %}
{% if storage_filter.startswith('-') %}
AND ("bricktracker_sets"."storage" IS NULL OR "bricktracker_sets"."storage" != '{{ storage_filter[1:] }}')
{% else %}
AND "bricktracker_sets"."storage" = '{{ storage_filter }}'
{% endif %}
{% endif %}
{% if purchase_location_filter %}
{% if purchase_location_filter.startswith('-') %}
AND ("bricktracker_sets"."purchase_location" IS NULL OR "bricktracker_sets"."purchase_location" != '{{ purchase_location_filter[1:] }}')
{% else %}
AND "bricktracker_sets"."purchase_location" = '{{ purchase_location_filter }}'
{% endif %}
{% endif %}
{% if status_filter %}
{% if status_filter == 'has-missing' %}
@@ -52,7 +68,13 @@ AND NOT EXISTS (
{% endif %}
{% if owner_filter %}
{% if owner_filter.startswith('owner-') %}
{% if owner_filter.startswith('-owner-') %}
AND NOT EXISTS (
SELECT 1 FROM "bricktracker_set_owners"
WHERE "bricktracker_set_owners"."id" = "bricktracker_sets"."id"
AND "bricktracker_set_owners"."{{ owner_filter[1:].replace('-', '_') }}" = 1
)
{% elif owner_filter.startswith('owner-') %}
AND EXISTS (
SELECT 1 FROM "bricktracker_set_owners"
WHERE "bricktracker_set_owners"."id" = "bricktracker_sets"."id"
@@ -62,7 +84,13 @@ AND EXISTS (
{% endif %}
{% if tag_filter %}
{% if tag_filter.startswith('tag-') %}
{% if tag_filter.startswith('-tag-') %}
AND NOT EXISTS (
SELECT 1 FROM "bricktracker_set_tags"
WHERE "bricktracker_set_tags"."id" = "bricktracker_sets"."id"
AND "bricktracker_set_tags"."{{ tag_filter[1:].replace('-', '_') }}" = 1
)
{% elif tag_filter.startswith('tag-') %}
AND EXISTS (
SELECT 1 FROM "bricktracker_set_tags"
WHERE "bricktracker_set_tags"."id" = "bricktracker_sets"."id"
@@ -91,28 +91,52 @@ AND (LOWER("rebrickable_sets"."name") LIKE LOWER('%{{ search_query }}%')
{% endif %}
{% if theme_filter %}
{% if theme_filter is string and theme_filter.startswith('-') %}
AND "rebrickable_sets"."theme_id" != {{ theme_filter[1:] }}
{% else %}
AND "rebrickable_sets"."theme_id" = {{ theme_filter }}
{% endif %}
{% endif %}
{% if year_filter %}
{% if year_filter is string and year_filter.startswith('-') %}
AND "rebrickable_sets"."year" != {{ year_filter[1:] }}
{% else %}
AND "rebrickable_sets"."year" = {{ year_filter }}
{% endif %}
{% endif %}
{% if storage_filter %}
{% if storage_filter.startswith('-') %}
AND NOT EXISTS (
SELECT 1 FROM "bricktracker_sets" bs_filter
WHERE bs_filter."set" = "rebrickable_sets"."set"
AND bs_filter."storage" = '{{ storage_filter[1:] }}'
)
{% else %}
AND EXISTS (
SELECT 1 FROM "bricktracker_sets" bs_filter
WHERE bs_filter."set" = "rebrickable_sets"."set"
AND bs_filter."storage" = '{{ storage_filter }}'
)
{% endif %}
{% endif %}
{% if purchase_location_filter %}
{% if purchase_location_filter.startswith('-') %}
AND NOT EXISTS (
SELECT 1 FROM "bricktracker_sets" bs_filter
WHERE bs_filter."set" = "rebrickable_sets"."set"
AND bs_filter."purchase_location" = '{{ purchase_location_filter[1:] }}'
)
{% else %}
AND EXISTS (
SELECT 1 FROM "bricktracker_sets" bs_filter
WHERE bs_filter."set" = "rebrickable_sets"."set"
AND bs_filter."purchase_location" = '{{ purchase_location_filter }}'
)
{% endif %}
{% endif %}
{% if status_filter %}
{% if status_filter == 'has-storage' %}
+62 -22
View File
@@ -67,28 +67,33 @@ class BrickGridFilter {
// Build filters
for (const select of this.selects) {
if (select.value != "") {
// Get the actual filter value (includes "-" prefix if toggle is in NOT mode)
const filterValue = typeof BrickFilterToggle !== 'undefined'
? BrickFilterToggle.getFilterValue(select)
: select.value;
if (filterValue != "") {
// Multi-attribute filter
switch (select.dataset.filter) {
// List contains values
case "value":
options.filters.push({
attribute: select.dataset.filterAttribute,
value: select.value,
value: filterValue,
})
break;
// List contains metadata attribute name, looking for true/false
case "metadata":
if (select.value.startsWith("-")) {
if (filterValue.startsWith("-")) {
options.filters.push({
attribute: select.value.substring(1),
attribute: filterValue.substring(1),
bool: true,
value: "0"
})
} else {
options.filters.push({
attribute: select.value,
attribute: filterValue,
bool: true,
value: "1"
});
@@ -130,23 +135,58 @@ class BrickGridFilter {
// Value check
// For consolidated cards, attributes may be comma or pipe-separated (e.g., "storage1,storage2" or "storage1|storage2")
else if (attribute == null) {
// Hide if attribute is missing
current.parentElement.classList.add("d-none");
return;
} else if (attribute.includes(',') || attribute.includes('|')) {
// Handle comma or pipe-separated values (consolidated cards)
const separator = attribute.includes('|') ? '|' : ',';
const values = attribute.split(separator).map(v => v.trim());
if (!values.includes(filter.value)) {
current.parentElement.classList.add("d-none");
return;
}
} else {
// Handle single values (regular cards)
if (attribute != filter.value) {
current.parentElement.classList.add("d-none");
return;
else {
// Check if this is a NOT filter (value starts with "-")
const isNot = filter.value.startsWith('-');
const actualValue = isNot ? filter.value.substring(1) : filter.value;
if (attribute == null) {
// If attribute is missing
if (isNot) {
// NOT filter: missing attribute means it doesn't match, so SHOW it
// (e.g., NOT "Basement" and has no storage = show)
// Continue to next filter
} else {
// Regular filter: missing attribute means hide
current.parentElement.classList.add("d-none");
return;
}
} else if (attribute.includes(',') || attribute.includes('|')) {
// Handle comma or pipe-separated values (consolidated cards)
const separator = attribute.includes('|') ? '|' : ',';
const values = attribute.split(separator).map(v => v.trim());
const hasValue = values.includes(actualValue);
if (isNot) {
// NOT filter: hide if ANY of the values match
if (hasValue) {
current.parentElement.classList.add("d-none");
return;
}
} else {
// Regular filter: hide if NONE of the values match
if (!hasValue) {
current.parentElement.classList.add("d-none");
return;
}
}
} else {
// Handle single values (regular cards)
const matches = (attribute == actualValue);
if (isNot) {
// NOT filter: hide if it matches
if (matches) {
current.parentElement.classList.add("d-none");
return;
}
} else {
// Regular filter: hide if it doesn't match
if (!matches) {
current.parentElement.classList.add("d-none");
return;
}
}
}
}
}
+117
View File
@@ -0,0 +1,117 @@
// Filter toggle for NOT filtering
class BrickFilterToggle {
constructor() {
// Find all filter toggle buttons
this.toggles = document.querySelectorAll('.filter-toggle');
// Initialize each toggle
this.toggles.forEach(toggle => {
this.initializeToggle(toggle);
});
}
initializeToggle(toggle) {
const targetId = toggle.dataset.filterTarget;
const targetSelect = document.getElementById(targetId);
if (!targetSelect) {
console.error(`Filter toggle: Target select #${targetId} not found`);
return;
}
// Check if we need to initialize in NOT mode based on URL parameters
const urlParams = new URLSearchParams(window.location.search);
const filterParam = this.getFilterParamName(targetId);
const filterValue = urlParams.get(filterParam);
// Initialize the NOT mode flag
if (filterValue && filterValue.startsWith('-')) {
targetSelect.dataset.notMode = 'true';
this.setToggleState(toggle, 'not-equals');
} else {
targetSelect.dataset.notMode = 'false';
this.setToggleState(toggle, 'equals');
}
// Add click event listener to toggle button
toggle.addEventListener('click', () => {
this.handleToggleClick(toggle, targetSelect);
});
// Add change event listener to the select
targetSelect.addEventListener('change', () => {
// If select is cleared (empty value), reset toggle to equals mode
const selectValue = targetSelect.options[targetSelect.selectedIndex]?.value || '';
if (!selectValue) {
targetSelect.dataset.notMode = 'false';
this.setToggleState(toggle, 'equals');
}
});
}
getFilterParamName(selectId) {
// Map select IDs to URL parameter names
const mapping = {
'grid-status': 'status',
'grid-theme': 'theme',
'grid-owner': 'owner',
'grid-storage': 'storage',
'grid-purchase-location': 'purchase_location',
'grid-tag': 'tag',
'grid-year': 'year'
};
return mapping[selectId] || selectId.replace('grid-', '');
}
handleToggleClick(toggle, targetSelect) {
const selectValue = targetSelect.options[targetSelect.selectedIndex]?.value || '';
// Don't toggle if no value is selected
if (!selectValue) {
return;
}
// Toggle the NOT mode
const isNotMode = targetSelect.dataset.notMode === 'true';
targetSelect.dataset.notMode = isNotMode ? 'false' : 'true';
// Update toggle button visual state
this.setToggleState(toggle, isNotMode ? 'equals' : 'not-equals');
// Trigger change event on the select to update the grid filter
targetSelect.dispatchEvent(new Event('change'));
}
setToggleState(toggle, mode) {
toggle.dataset.filterMode = mode;
const icon = toggle.querySelector('i');
if (mode === 'not-equals') {
icon.className = 'ri-indeterminate-circle-line';
toggle.classList.remove('btn-outline-secondary');
toggle.classList.add('btn-outline-danger');
toggle.title = 'NOT equals (click to toggle)';
} else {
icon.className = 'ri-equal-line';
toggle.classList.remove('btn-outline-danger');
toggle.classList.add('btn-outline-secondary');
toggle.title = 'Equals (click to toggle)';
}
}
// Helper method to get the actual filter value (with "-" prefix if in NOT mode)
static getFilterValue(select) {
const selectValue = select.options[select.selectedIndex]?.value || '';
const isNotMode = select.dataset.notMode === 'true';
if (selectValue && isNotMode && !selectValue.startsWith('-')) {
return '-' + selectValue;
}
return selectValue;
}
}
// Initialize when DOM is ready
document.addEventListener("DOMContentLoaded", () => {
new BrickFilterToggle();
});
+49 -23
View File
@@ -145,12 +145,15 @@ function initializeFilterDropdowns() {
// Set filter dropdown values from URL parameters
const urlParams = new URLSearchParams(window.location.search);
// Helper function to strip "-" prefix from filter values
const stripNotPrefix = (value) => value && value.startsWith('-') ? value.substring(1) : value;
// Set each filter dropdown value if the parameter exists
const yearParam = urlParams.get('year');
if (yearParam) {
const yearDropdown = document.getElementById('grid-year');
if (yearDropdown) {
yearDropdown.value = yearParam;
yearDropdown.value = stripNotPrefix(yearParam);
}
}
@@ -158,14 +161,15 @@ function initializeFilterDropdowns() {
if (themeParam) {
const themeDropdown = document.getElementById('grid-theme');
if (themeDropdown) {
const cleanTheme = stripNotPrefix(themeParam);
// Try to set the theme value directly first (for theme names)
themeDropdown.value = themeParam;
themeDropdown.value = cleanTheme;
// If that didn't work and the param is numeric (theme ID),
// try to find the corresponding theme name by looking at cards
if (themeDropdown.value !== themeParam && /^\d+$/.test(themeParam)) {
if (themeDropdown.value !== cleanTheme && /^\d+$/.test(cleanTheme)) {
// Look for a card with this theme ID and get its theme name
const cardWithTheme = document.querySelector(`[data-theme-id="${themeParam}"]`);
const cardWithTheme = document.querySelector(`[data-theme-id="${cleanTheme}"]`);
if (cardWithTheme) {
const themeName = cardWithTheme.getAttribute('data-theme');
if (themeName) {
@@ -180,7 +184,7 @@ function initializeFilterDropdowns() {
if (statusParam) {
const statusDropdown = document.getElementById('grid-status');
if (statusDropdown) {
statusDropdown.value = statusParam;
statusDropdown.value = stripNotPrefix(statusParam);
}
}
@@ -188,7 +192,7 @@ function initializeFilterDropdowns() {
if (ownerParam) {
const ownerDropdown = document.getElementById('grid-owner');
if (ownerDropdown) {
ownerDropdown.value = ownerParam;
ownerDropdown.value = stripNotPrefix(ownerParam);
}
}
@@ -196,7 +200,7 @@ function initializeFilterDropdowns() {
if (purchaseLocationParam) {
const purchaseLocationDropdown = document.getElementById('grid-purchase-location');
if (purchaseLocationDropdown) {
purchaseLocationDropdown.value = purchaseLocationParam;
purchaseLocationDropdown.value = stripNotPrefix(purchaseLocationParam);
}
}
@@ -204,7 +208,7 @@ function initializeFilterDropdowns() {
if (storageParam) {
const storageDropdown = document.getElementById('grid-storage');
if (storageDropdown) {
storageDropdown.value = storageParam;
storageDropdown.value = stripNotPrefix(storageParam);
}
}
@@ -212,7 +216,7 @@ function initializeFilterDropdowns() {
if (tagParam) {
const tagDropdown = document.getElementById('grid-tag');
if (tagDropdown) {
tagDropdown.value = tagParam;
tagDropdown.value = stripNotPrefix(tagParam);
}
}
}
@@ -222,6 +226,9 @@ function initializeClientSideFilterDropdowns() {
const urlParams = new URLSearchParams(window.location.search);
let needsFiltering = false;
// Helper function to strip "-" prefix from filter values
const stripNotPrefix = (value) => value && value.startsWith('-') ? value.substring(1) : value;
// Check if we have any filter parameters to avoid flash of all content
const hasFilterParams = urlParams.has('year') || urlParams.has('theme') || urlParams.has('storage') || urlParams.has('purchase_location');
@@ -238,7 +245,7 @@ function initializeClientSideFilterDropdowns() {
if (yearParam) {
const yearDropdown = document.getElementById('grid-year');
if (yearDropdown) {
yearDropdown.value = yearParam;
yearDropdown.value = stripNotPrefix(yearParam);
needsFiltering = true;
}
}
@@ -248,16 +255,17 @@ function initializeClientSideFilterDropdowns() {
if (themeParam) {
const themeDropdown = document.getElementById('grid-theme');
if (themeDropdown) {
if (/^\d+$/.test(themeParam)) {
const cleanTheme = stripNotPrefix(themeParam);
if (/^\d+$/.test(cleanTheme)) {
// Theme parameter is an ID, need to convert to theme name by looking at cards
const themeNameFromId = findThemeNameById(themeParam);
const themeNameFromId = findThemeNameById(cleanTheme);
if (themeNameFromId) {
themeDropdown.value = themeNameFromId;
needsFiltering = true;
}
} else {
// Theme parameter is already a name
themeDropdown.value = themeParam.toLowerCase();
themeDropdown.value = cleanTheme.toLowerCase();
needsFiltering = true;
}
}
@@ -268,7 +276,7 @@ function initializeClientSideFilterDropdowns() {
if (storageParam) {
const storageDropdown = document.getElementById('grid-storage');
if (storageDropdown) {
storageDropdown.value = storageParam;
storageDropdown.value = stripNotPrefix(storageParam);
needsFiltering = true;
}
}
@@ -278,7 +286,7 @@ function initializeClientSideFilterDropdowns() {
if (purchaseLocationParam) {
const purchaseLocationDropdown = document.getElementById('grid-purchase-location');
if (purchaseLocationDropdown) {
purchaseLocationDropdown.value = purchaseLocationParam;
purchaseLocationDropdown.value = stripNotPrefix(purchaseLocationParam);
needsFiltering = true;
}
}
@@ -343,14 +351,30 @@ function setupPaginationFilterDropdowns() {
function performServerFilter() {
const currentUrl = new URL(window.location);
// Get all filter values
const statusFilter = document.getElementById('grid-status')?.value || '';
const themeFilter = document.getElementById('grid-theme')?.value || '';
const yearFilter = document.getElementById('grid-year')?.value || '';
const ownerFilter = document.getElementById('grid-owner')?.value || '';
const purchaseLocationFilter = document.getElementById('grid-purchase-location')?.value || '';
const storageFilter = document.getElementById('grid-storage')?.value || '';
const tagFilter = document.getElementById('grid-tag')?.value || '';
// Get all filter values (using BrickFilterToggle helper to include "-" prefix if in NOT mode)
const statusSelect = document.getElementById('grid-status');
const themeSelect = document.getElementById('grid-theme');
const yearSelect = document.getElementById('grid-year');
const ownerSelect = document.getElementById('grid-owner');
const purchaseLocationSelect = document.getElementById('grid-purchase-location');
const storageSelect = document.getElementById('grid-storage');
const tagSelect = document.getElementById('grid-tag');
// Helper to safely get filter value with NOT mode support
const getFilterValue = (select) => {
if (!select) return '';
return typeof BrickFilterToggle !== 'undefined'
? BrickFilterToggle.getFilterValue(select)
: select.value;
};
const statusFilter = getFilterValue(statusSelect);
const themeFilter = getFilterValue(themeSelect);
const yearFilter = getFilterValue(yearSelect);
const ownerFilter = getFilterValue(ownerSelect);
const purchaseLocationFilter = getFilterValue(purchaseLocationSelect);
const storageFilter = getFilterValue(storageSelect);
const tagFilter = getFilterValue(tagSelect);
// Update URL parameters
if (statusFilter) {
@@ -746,6 +770,8 @@ function initializeClearFiltersButton() {
const dropdown = document.getElementById(dropdownId);
if (dropdown) {
dropdown.value = '';
// Trigger change event to reset toggle button state
dropdown.dispatchEvent(new Event('change'));
}
});
+1
View File
@@ -84,6 +84,7 @@
<!-- BrickTracker scripts -->
<script src="{{ url_for('static', filename='scripts/collapsible-state.js') }}"></script>
<script src="{{ url_for('static', filename='scripts/changer.js') }}"></script>
<script src="{{ url_for('static', filename='scripts/grid/filter_toggle.js') }}"></script>
<script src="{{ url_for('static', filename='scripts/grid/filter.js') }}"></script>
<script src="{{ url_for('static', filename='scripts/grid/grid.js') }}"></script>
<script src="{{ url_for('static', filename='scripts/grid/sort.js') }}"></script>
+11
View File
@@ -64,3 +64,14 @@
</div>
{% endif %}
{% endmacro %}
{% macro filter_toggle(filter_id) %}
<button type="button"
class="btn btn-outline-secondary filter-toggle"
id="{{ filter_id }}-toggle"
data-filter-target="{{ filter_id }}"
data-filter-mode="equals"
title="Toggle between equals and not equals">
<i class="ri-equal-line"></i>
</button>
{% endmacro %}
+20 -17
View File
@@ -1,3 +1,4 @@
{% import 'macro/form.html' as form %}
<div id="grid-filter" class="collapse {% if config['SHOW_GRID_FILTERS'] %}show{% endif %} row row-cols-lg-auto g-1 justify-content-center align-items-center pb-2">
<div class="col-12 flex-grow-1">
<label class="visually-hidden" for="grid-status">Status</label>
@@ -8,26 +9,22 @@
autocomplete="off">
<option value="" {% if not current_status_filter %}selected{% endif %}>All</option>
{% if not config['HIDE_TABLE_MISSING_PARTS'] %}
<option value="has-missing" {% if current_status_filter == 'has-missing' %}selected{% endif %}>Has missing pieces</option>
<option value="-has-missing" {% if current_status_filter == '-has-missing' %}selected{% endif %}>Has NO missing pieces</option>
<option value="has-missing" {% if current_status_filter == 'has-missing' or current_status_filter == '-has-missing' %}selected{% endif %}>Missing pieces</option>
{% endif %}
{% if not config['HIDE_TABLE_DAMAGED_PARTS'] %}
<option value="has-damaged" {% if current_status_filter == 'has-damaged' %}selected{% endif %}>Has damaged pieces</option>
<option value="-has-damaged" {% if current_status_filter == '-has-damaged' %}selected{% endif %}>Has NO damaged pieces</option>
<option value="has-damaged" {% if current_status_filter == 'has-damaged' or current_status_filter == '-has-damaged' %}selected{% endif %}>Damaged pieces</option>
{% endif %}
{% if not config['HIDE_SET_INSTRUCTIONS'] %}
<option value="-has-missing-instructions" {% if current_status_filter == '-has-missing-instructions' %}selected{% endif %}>Has instructions</option>
<option value="has-missing-instructions" {% if current_status_filter == 'has-missing-instructions' %}selected{% endif %}>Is MISSING instructions</option>
<option value="has-missing-instructions" {% if current_status_filter == 'has-missing-instructions' or current_status_filter == '-has-missing-instructions' %}selected{% endif %}>Missing instructions</option>
{% endif %}
{% if brickset_storages | length %}
<option value="has-storage" {% if current_status_filter == 'has-storage' %}selected{% endif %}>Is in storage</option>
<option value="-has-storage" {% if current_status_filter == '-has-storage' %}selected{% endif %}>Is NOT in storage</option>
<option value="has-storage" {% if current_status_filter == 'has-storage' or current_status_filter == '-has-storage' %}selected{% endif %}>In storage</option>
{% endif %}
{% for status in brickset_statuses %}
<option value="{{ status.as_dataset() }}" {% if current_status_filter == status.as_dataset() %}selected{% endif %}>{{ status.fields.name }}</option>
<option value="-{{ status.as_dataset() }}" {% if current_status_filter == ('-' + status.as_dataset()) %}selected{% endif %}>NOT: {{ status.fields.name }}</option>
<option value="{{ status.as_dataset() }}" {% if current_status_filter == status.as_dataset() or current_status_filter == ('-' + status.as_dataset()) %}selected{% endif %}>{{ status.fields.name }}</option>
{% endfor %}
</select>
{{ form.filter_toggle('grid-status') }}
</div>
</div>
<div class="col-12 flex-grow-1">
@@ -39,9 +36,10 @@
autocomplete="off">
<option value="" {% if not current_theme_filter %}selected{% endif %}>All</option>
{% for theme in collection.themes %}
<option value="{{ theme | lower }}" {% if current_theme_filter == (theme | lower) %}selected{% endif %}>{{ theme }}</option>
<option value="{{ theme | lower }}" {% if current_theme_filter == (theme | lower) or current_theme_filter == ('-' + (theme | lower)) %}selected{% endif %}>{{ theme }}</option>
{% endfor %}
</select>
{{ form.filter_toggle('grid-theme') }}
</div>
</div>
{% if brickset_owners | length %}
@@ -57,12 +55,13 @@
<option value="{{ owner.as_dataset() }}" {% if current_owner_filter == owner.as_dataset() %}selected{% endif %}>{{ owner.fields.name }}</option>
{% endfor %}
</select>
{{ form.filter_toggle('grid-owner') }}
</div>
</div>
{% endif %}
{% if brickset_purchase_locations | length %}
<div class="col-12 flex-grow-1">
<label class="visually-hidden" for="grid-owner">Purchase location</label>
<label class="visually-hidden" for="grid-purchase-location">Purchase location</label>
<div class="input-group">
<span class="input-group-text"><i class="ri-building-line"></i><span class="ms-1 d-none d-md-inline"> Purchase location</span></span>
<select id="grid-purchase-location" class="form-select"
@@ -70,9 +69,10 @@
autocomplete="off">
<option value="" {% if not current_purchase_location_filter %}selected{% endif %}>All</option>
{% for purchase_location in brickset_purchase_locations %}
<option value="{{ purchase_location.fields.id }}" {% if current_purchase_location_filter == purchase_location.fields.id %}selected{% endif %}>{{ purchase_location.fields.name }}</option>
<option value="{{ purchase_location.fields.id }}" {% if current_purchase_location_filter == purchase_location.fields.id or current_purchase_location_filter == ('-' + purchase_location.fields.id) %}selected{% endif %}>{{ purchase_location.fields.name }}</option>
{% endfor %}
</select>
{{ form.filter_toggle('grid-purchase-location') }}
</div>
</div>
{% endif %}
@@ -80,7 +80,7 @@
<div class="w-100"></div>
{% if brickset_storages | length %}
<div class="col-12 flex-grow-1">
<label class="visually-hidden" for="grid-owner">Storage</label>
<label class="visually-hidden" for="grid-storage">Storage</label>
<div class="input-group">
<span class="input-group-text"><i class="ri-archive-2-line"></i><span class="ms-1 d-none d-md-inline"> Storage</span></span>
<select id="grid-storage" class="form-select"
@@ -88,9 +88,10 @@
autocomplete="off">
<option value="" {% if not current_storage_filter %}selected{% endif %}>All</option>
{% for storage in brickset_storages %}
<option value="{{ storage.fields.id }}" {% if current_storage_filter == storage.fields.id %}selected{% endif %}>{{ storage.fields.name }}</option>
<option value="{{ storage.fields.id }}" {% if current_storage_filter == storage.fields.id or current_storage_filter == ('-' + storage.fields.id) %}selected{% endif %}>{{ storage.fields.name }}</option>
{% endfor %}
</select>
{{ form.filter_toggle('grid-storage') }}
</div>
</div>
{% endif %}
@@ -104,9 +105,10 @@
autocomplete="off">
<option value="" {% if not current_tag_filter %}selected{% endif %}>All</option>
{% for tag in brickset_tags %}
<option value="{{ tag.as_dataset() }}" {% if current_tag_filter == tag.as_dataset() %}selected{% endif %}>{{ tag.fields.name }}</option>
<option value="{{ tag.as_dataset() }}" {% if current_tag_filter == tag.as_dataset() or current_tag_filter == ('-' + tag.as_dataset()) %}selected{% endif %}>{{ tag.fields.name }}</option>
{% endfor %}
</select>
{{ form.filter_toggle('grid-tag') }}
</div>
</div>
{% endif %}
@@ -119,9 +121,10 @@
autocomplete="off">
<option value="" {% if not current_year_filter %}selected{% endif %}>All</option>
{% for year in collection.years %}
<option value="{{ year }}" {% if current_year_filter == year %}selected{% endif %}>{{ year }}</option>
<option value="{{ year }}" {% if current_year_filter == year or current_year_filter == ('-' + year|string) %}selected{% endif %}>{{ year }}</option>
{% endfor %}
</select>
{{ form.filter_toggle('grid-year') }}
</div>
</div>
<div class="col-12 col-lg-auto">