mirror of
https://gitea.baerentsen.space/FrederikBaerentsen/BrickTracker.git
synced 2026-02-21 15:59:05 -06:00
Fixed sorting and filtering on /sets.
This commit is contained in:
@@ -13,6 +13,8 @@ from .set_storage_list import BrickSetStorageList
|
||||
from .set_tag import BrickSetTag
|
||||
from .set_tag_list import BrickSetTagList
|
||||
from .set import BrickSet
|
||||
from .theme_list import BrickThemeList
|
||||
from .instructions_list import BrickInstructionsList
|
||||
|
||||
|
||||
# All the sets from the database
|
||||
@@ -56,11 +58,31 @@ class BrickSetList(BrickRecordList[BrickSet]):
|
||||
page: int = 1,
|
||||
per_page: int = 50,
|
||||
sort_field: str | None = None,
|
||||
sort_order: str = 'asc'
|
||||
sort_order: str = 'asc',
|
||||
status_filter: str | None = None,
|
||||
theme_filter: str | None = None,
|
||||
owner_filter: str | None = None,
|
||||
purchase_location_filter: str | None = None,
|
||||
storage_filter: str | None = None,
|
||||
tag_filter: str | None = None
|
||||
) -> tuple[Self, int]:
|
||||
# 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 any filters are applied
|
||||
has_filters = any([status_filter, theme_id_filter, owner_filter, purchase_location_filter, storage_filter, tag_filter])
|
||||
|
||||
# Prepare filter context
|
||||
filter_context = {
|
||||
'search_query': search_query,
|
||||
'status_filter': status_filter,
|
||||
'theme_filter': theme_id_filter, # Use converted theme ID
|
||||
'owner_filter': owner_filter,
|
||||
'purchase_location_filter': purchase_location_filter,
|
||||
'storage_filter': storage_filter,
|
||||
'tag_filter': tag_filter,
|
||||
'owners': BrickSetOwnerList.as_columns(),
|
||||
'statuses': BrickSetStatusList.as_columns(),
|
||||
'tags': BrickSetTagList.as_columns(),
|
||||
@@ -71,22 +93,107 @@ class BrickSetList(BrickRecordList[BrickSet]):
|
||||
'set': '"rebrickable_sets"."set"',
|
||||
'name': '"rebrickable_sets"."name"',
|
||||
'year': '"rebrickable_sets"."year"',
|
||||
'parts': '"rebrickable_sets"."number_of_parts"'
|
||||
'parts': '"rebrickable_sets"."number_of_parts"',
|
||||
'theme': '"rebrickable_sets"."theme_id"',
|
||||
'minifigures': '"total_minifigures"', # Use the alias from the SQL query
|
||||
'missing': '"total_missing"', # Use the alias from the SQL query
|
||||
'damaged': '"total_damaged"', # Use the alias from the SQL query
|
||||
'purchase-date': '"bricktracker_sets"."purchase_date"',
|
||||
'purchase-price': '"bricktracker_sets"."purchase_price"'
|
||||
}
|
||||
|
||||
# Choose query based on whether filters are applied
|
||||
query_to_use = 'set/list/all_filtered' if has_filters else self.all_query
|
||||
|
||||
# Handle instructions filtering separately (post-SQL filtering)
|
||||
instructions_filter = None
|
||||
if status_filter in ['has-missing-instructions', '-has-missing-instructions']:
|
||||
instructions_filter = status_filter
|
||||
# Remove from SQL context to avoid SQL errors
|
||||
filter_context['status_filter'] = None
|
||||
# Recalculate has_filters without instructions
|
||||
has_filters = any([theme_id_filter, owner_filter, purchase_location_filter, storage_filter, tag_filter])
|
||||
query_to_use = 'set/list/all_filtered' if has_filters else self.all_query
|
||||
|
||||
# Use the base pagination method with custom list method
|
||||
result, total_count = self.paginate(
|
||||
page=page,
|
||||
per_page=per_page,
|
||||
sort_field=sort_field,
|
||||
sort_order=sort_order,
|
||||
list_query=self.all_query,
|
||||
list_query=query_to_use,
|
||||
field_mapping=field_mapping,
|
||||
**filter_context
|
||||
)
|
||||
|
||||
# Apply instructions filtering after SQL query
|
||||
if instructions_filter:
|
||||
result, total_count = self._filter_by_instructions(result, total_count, instructions_filter, page, per_page)
|
||||
|
||||
# Populate themes for filter dropdown (always needed)
|
||||
result._populate_themes()
|
||||
|
||||
return result, total_count
|
||||
|
||||
def _populate_themes(self) -> None:
|
||||
"""Populate themes list from the current records"""
|
||||
themes = set()
|
||||
for record in self.records:
|
||||
if hasattr(record, 'theme') and hasattr(record.theme, 'name'):
|
||||
themes.add(record.theme.name)
|
||||
|
||||
self.themes = list(themes)
|
||||
self.themes.sort()
|
||||
|
||||
def _theme_name_to_id(self, theme_name: str) -> str | None:
|
||||
"""Convert a theme name to theme ID for filtering"""
|
||||
try:
|
||||
theme_list = BrickThemeList()
|
||||
for theme_id, theme in theme_list.themes.items():
|
||||
if theme.name.lower() == theme_name.lower():
|
||||
return str(theme_id)
|
||||
return None
|
||||
except Exception:
|
||||
# If themes can't be loaded, return None to disable theme filtering
|
||||
return None
|
||||
|
||||
def _filter_by_instructions(self, result_list: Self, total_count: int, instructions_filter: str, page: int, per_page: int) -> tuple[Self, int]:
|
||||
"""Filter sets by instruction file existence (post-SQL filtering)"""
|
||||
try:
|
||||
# Load instructions list
|
||||
instructions_list = BrickInstructionsList()
|
||||
instruction_sets = set(instructions_list.sets.keys())
|
||||
|
||||
# Filter the records
|
||||
filtered_records = []
|
||||
for record in result_list.records:
|
||||
set_id = record.fields.set
|
||||
has_instructions = set_id in instruction_sets
|
||||
|
||||
if instructions_filter == 'has-missing-instructions':
|
||||
# Show sets that are MISSING instructions
|
||||
if not has_instructions:
|
||||
filtered_records.append(record)
|
||||
elif instructions_filter == '-has-missing-instructions':
|
||||
# Show sets that HAVE instructions
|
||||
if has_instructions:
|
||||
filtered_records.append(record)
|
||||
|
||||
# Create new result with filtered records
|
||||
new_result = BrickSetList()
|
||||
new_result.records = filtered_records
|
||||
|
||||
# Note: This breaks proper pagination since we're filtering after SQL
|
||||
# The total_count and pagination will be approximate
|
||||
# For proper pagination, we'd need a database table for instructions
|
||||
# This will be implemented in future versions
|
||||
|
||||
return new_result, len(filtered_records)
|
||||
|
||||
except Exception:
|
||||
# If instructions can't be loaded, return original results
|
||||
return result_list, total_count
|
||||
|
||||
# Sets with a minifigure part damaged
|
||||
def damaged_minifigure(self, figure: str, /) -> Self:
|
||||
# Save the parameters to the fields
|
||||
|
||||
69
bricktracker/sql/set/list/all_filtered.sql
Normal file
69
bricktracker/sql/set/list/all_filtered.sql
Normal file
@@ -0,0 +1,69 @@
|
||||
{% extends 'set/base/full.sql' %}
|
||||
|
||||
{% block where %}
|
||||
WHERE 1=1
|
||||
{% if search_query %}
|
||||
AND (LOWER("rebrickable_sets"."name") LIKE LOWER('%{{ search_query }}%')
|
||||
OR LOWER("rebrickable_sets"."set") LIKE LOWER('%{{ search_query }}%'))
|
||||
{% endif %}
|
||||
|
||||
{% if theme_filter %}
|
||||
AND "rebrickable_sets"."theme_id" = '{{ theme_filter }}'
|
||||
{% endif %}
|
||||
|
||||
{% if storage_filter %}
|
||||
AND "bricktracker_sets"."storage" = '{{ storage_filter }}'
|
||||
{% endif %}
|
||||
|
||||
{% if purchase_location_filter %}
|
||||
AND "bricktracker_sets"."purchase_location" = '{{ purchase_location_filter }}'
|
||||
{% endif %}
|
||||
|
||||
{% if status_filter %}
|
||||
{% if status_filter == 'has-missing' %}
|
||||
AND IFNULL("problem_join"."total_missing", 0) > 0
|
||||
{% elif status_filter == '-has-missing' %}
|
||||
AND IFNULL("problem_join"."total_missing", 0) = 0
|
||||
{% elif status_filter == 'has-damaged' %}
|
||||
AND IFNULL("problem_join"."total_damaged", 0) > 0
|
||||
{% elif status_filter == '-has-damaged' %}
|
||||
AND IFNULL("problem_join"."total_damaged", 0) = 0
|
||||
{% elif status_filter == 'has-storage' %}
|
||||
AND "bricktracker_sets"."storage" IS NOT NULL AND "bricktracker_sets"."storage" != ''
|
||||
{% elif status_filter == '-has-storage' %}
|
||||
AND ("bricktracker_sets"."storage" IS NULL OR "bricktracker_sets"."storage" = '')
|
||||
{% elif status_filter.startswith('status-') %}
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM "bricktracker_set_statuses"
|
||||
WHERE "bricktracker_set_statuses"."id" = "bricktracker_sets"."id"
|
||||
AND "bricktracker_set_statuses"."{{ status_filter.replace('-', '_') }}" = 1
|
||||
)
|
||||
{% elif status_filter.startswith('-status-') %}
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM "bricktracker_set_statuses"
|
||||
WHERE "bricktracker_set_statuses"."id" = "bricktracker_sets"."id"
|
||||
AND "bricktracker_set_statuses"."{{ status_filter[1:].replace('-', '_') }}" = 1
|
||||
)
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if owner_filter %}
|
||||
{% if owner_filter.startswith('owner-') %}
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM "bricktracker_set_owners"
|
||||
WHERE "bricktracker_set_owners"."id" = "bricktracker_sets"."id"
|
||||
AND "bricktracker_set_owners"."{{ owner_filter.replace('-', '_') }}" = 1
|
||||
)
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if tag_filter %}
|
||||
{% if tag_filter.startswith('tag-') %}
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM "bricktracker_set_tags"
|
||||
WHERE "bricktracker_set_tags"."id" = "bricktracker_sets"."id"
|
||||
AND "bricktracker_set_tags"."{{ tag_filter.replace('-', '_') }}" = 1
|
||||
)
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -39,18 +39,32 @@ def list() -> str:
|
||||
# Get filter parameters from request
|
||||
search_query, sort_field, sort_order, page = get_request_params()
|
||||
|
||||
# Get filter parameters
|
||||
status_filter = request.args.get('status')
|
||||
theme_filter = request.args.get('theme')
|
||||
owner_filter = request.args.get('owner')
|
||||
purchase_location_filter = request.args.get('purchase_location')
|
||||
storage_filter = request.args.get('storage')
|
||||
tag_filter = request.args.get('tag')
|
||||
|
||||
# Get pagination configuration
|
||||
per_page, is_mobile = get_pagination_config('sets')
|
||||
use_pagination = per_page > 0
|
||||
|
||||
if use_pagination:
|
||||
# PAGINATION MODE - Server-side pagination with search
|
||||
# PAGINATION MODE - Server-side pagination with search and filters
|
||||
sets, total_count = BrickSetList().all_filtered_paginated(
|
||||
search_query=search_query,
|
||||
page=page,
|
||||
per_page=per_page,
|
||||
sort_field=sort_field,
|
||||
sort_order=sort_order
|
||||
sort_order=sort_order,
|
||||
status_filter=status_filter,
|
||||
theme_filter=theme_filter,
|
||||
owner_filter=owner_filter,
|
||||
purchase_location_filter=purchase_location_filter,
|
||||
storage_filter=storage_filter,
|
||||
tag_filter=tag_filter
|
||||
)
|
||||
|
||||
pagination_context = build_pagination_context(page, per_page, total_count, is_mobile)
|
||||
@@ -65,6 +79,12 @@ def list() -> str:
|
||||
'use_pagination': use_pagination,
|
||||
'current_sort': sort_field,
|
||||
'current_order': sort_order,
|
||||
'current_status_filter': status_filter,
|
||||
'current_theme_filter': theme_filter,
|
||||
'current_owner_filter': owner_filter,
|
||||
'current_purchase_location_filter': purchase_location_filter,
|
||||
'current_storage_filter': storage_filter,
|
||||
'current_tag_filter': tag_filter,
|
||||
'brickset_statuses': BrickSetStatusList.list(),
|
||||
**set_metadata_lists(as_class=True)
|
||||
}
|
||||
|
||||
@@ -57,6 +57,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
// Setup sort buttons for pagination mode
|
||||
setupPaginationSortButtons();
|
||||
|
||||
// Setup filter dropdowns for pagination mode
|
||||
setupPaginationFilterDropdowns();
|
||||
|
||||
} else {
|
||||
// ORIGINAL MODE - Grid search functionality is handled by existing grid scripts
|
||||
// No additional setup needed here
|
||||
@@ -105,4 +108,68 @@ function setupPaginationSortButtons() {
|
||||
window.location.href = currentUrl.toString();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setupPaginationFilterDropdowns() {
|
||||
// Filter dropdown functionality for pagination mode
|
||||
const filterDropdowns = document.querySelectorAll('#grid-filter select');
|
||||
|
||||
filterDropdowns.forEach(dropdown => {
|
||||
dropdown.addEventListener('change', () => {
|
||||
performServerFilter();
|
||||
});
|
||||
});
|
||||
|
||||
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 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 || '';
|
||||
|
||||
// Update URL parameters
|
||||
if (statusFilter) {
|
||||
currentUrl.searchParams.set('status', statusFilter);
|
||||
} else {
|
||||
currentUrl.searchParams.delete('status');
|
||||
}
|
||||
|
||||
if (themeFilter) {
|
||||
currentUrl.searchParams.set('theme', themeFilter);
|
||||
} else {
|
||||
currentUrl.searchParams.delete('theme');
|
||||
}
|
||||
|
||||
if (ownerFilter) {
|
||||
currentUrl.searchParams.set('owner', ownerFilter);
|
||||
} else {
|
||||
currentUrl.searchParams.delete('owner');
|
||||
}
|
||||
|
||||
if (purchaseLocationFilter) {
|
||||
currentUrl.searchParams.set('purchase_location', purchaseLocationFilter);
|
||||
} else {
|
||||
currentUrl.searchParams.delete('purchase_location');
|
||||
}
|
||||
|
||||
if (storageFilter) {
|
||||
currentUrl.searchParams.set('storage', storageFilter);
|
||||
} else {
|
||||
currentUrl.searchParams.delete('storage');
|
||||
}
|
||||
|
||||
if (tagFilter) {
|
||||
currentUrl.searchParams.set('tag', tagFilter);
|
||||
} else {
|
||||
currentUrl.searchParams.delete('tag');
|
||||
}
|
||||
|
||||
// Reset to page 1 when filtering
|
||||
currentUrl.searchParams.set('page', '1');
|
||||
window.location.href = currentUrl.toString();
|
||||
}
|
||||
}
|
||||
@@ -6,26 +6,26 @@
|
||||
<select id="grid-status" class="form-select"
|
||||
data-filter="metadata"
|
||||
autocomplete="off">
|
||||
<option value="" selected>All</option>
|
||||
<option value="" {% if not current_status_filter %}selected{% endif %}>All</option>
|
||||
{% if not config['HIDE_TABLE_MISSING_PARTS'] %}
|
||||
<option value="has-missing">Has missing pieces</option>
|
||||
<option value="-has-missing">Has NO missing pieces</option>
|
||||
<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>
|
||||
{% endif %}
|
||||
{% if not config['HIDE_TABLE_DAMAGED_PARTS'] %}
|
||||
<option value="has-damaged">Has damaged pieces</option>
|
||||
<option value="-has-damaged">Has NO damaged pieces</option>
|
||||
<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>
|
||||
{% endif %}
|
||||
{% if not config['HIDE_SET_INSTRUCTIONS'] %}
|
||||
<option value="-has-missing-instructions">Has instructions</option>
|
||||
<option value="has-missing-instructions">Is MISSING instructions</option>
|
||||
<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>
|
||||
{% endif %}
|
||||
{% if brickset_storages | length %}
|
||||
<option value="has-storage">Is in storage</option>
|
||||
<option value="-has-storage">Is NOT in storage</option>
|
||||
<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>
|
||||
{% endif %}
|
||||
{% for status in brickset_statuses %}
|
||||
<option value="{{ status.as_dataset() }}">{{ status.fields.name }}</option>
|
||||
<option value="-{{ status.as_dataset() }}">NOT: {{ status.fields.name }}</option>
|
||||
<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>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@@ -37,9 +37,9 @@
|
||||
<select id="grid-theme" class="form-select"
|
||||
data-filter="value" data-filter-attribute="theme"
|
||||
autocomplete="off">
|
||||
<option value="" selected>All</option>
|
||||
<option value="" {% if not current_theme_filter %}selected{% endif %}>All</option>
|
||||
{% for theme in collection.themes %}
|
||||
<option value="{{ theme | lower }}">{{ theme }}</option>
|
||||
<option value="{{ theme | lower }}" {% if current_theme_filter == (theme | lower) %}selected{% endif %}>{{ theme }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@@ -52,9 +52,9 @@
|
||||
<select id="grid-owner" class="form-select"
|
||||
data-filter="metadata"
|
||||
autocomplete="off">
|
||||
<option value="" selected>All</option>
|
||||
<option value="" {% if not current_owner_filter %}selected{% endif %}>All</option>
|
||||
{% for owner in brickset_owners %}
|
||||
<option value="{{ owner.as_dataset() }}">{{ owner.fields.name }}</option>
|
||||
<option value="{{ owner.as_dataset() }}" {% if current_owner_filter == owner.as_dataset() %}selected{% endif %}>{{ owner.fields.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@@ -68,9 +68,9 @@
|
||||
<select id="grid-purchase-location" class="form-select"
|
||||
data-filter="value" data-filter-attribute="purchase-location"
|
||||
autocomplete="off">
|
||||
<option value="" selected>All</option>
|
||||
<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 }}">{{ purchase_location.fields.name }}</option>
|
||||
<option value="{{ purchase_location.fields.id }}" {% if current_purchase_location_filter == purchase_location.fields.id %}selected{% endif %}>{{ purchase_location.fields.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@@ -84,9 +84,9 @@
|
||||
<select id="grid-storage" class="form-select"
|
||||
data-filter="value" data-filter-attribute="storage"
|
||||
autocomplete="off">
|
||||
<option value="" selected>All</option>
|
||||
<option value="" {% if not current_storage_filter %}selected{% endif %}>All</option>
|
||||
{% for storage in brickset_storages %}
|
||||
<option value="{{ storage.fields.id }}">{{ storage.fields.name }}</option>
|
||||
<option value="{{ storage.fields.id }}" {% if current_storage_filter == storage.fields.id %}selected{% endif %}>{{ storage.fields.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@@ -100,9 +100,9 @@
|
||||
<select id="grid-tag" class="form-select"
|
||||
data-filter="metadata"
|
||||
autocomplete="off">
|
||||
<option value="" selected>All</option>
|
||||
<option value="" {% if not current_tag_filter %}selected{% endif %}>All</option>
|
||||
{% for tag in brickset_tags %}
|
||||
<option value="{{ tag.as_dataset() }}">{{ tag.fields.name }}</option>
|
||||
<option value="{{ tag.as_dataset() }}" {% if current_tag_filter == tag.as_dataset() %}selected{% endif %}>{{ tag.fields.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -6,19 +6,14 @@
|
||||
data-sort-attribute="set" data-sort-natural="true"><i class="ri-hashtag"></i><span class="d-none d-md-inline"> Set</span></button>
|
||||
<button id="sort-name" type="button" class="btn btn-outline-primary mb-2"
|
||||
data-sort-attribute="name"><i class="ri-pencil-line"></i><span class="d-none d-md-inline"> Name</span></button>
|
||||
{% if not use_pagination %}
|
||||
<button id="sort-theme" type="button" class="btn btn-outline-primary mb-2"
|
||||
data-sort-attribute="theme"><i class="ri-price-tag-3-line"></i><span class="d-none d-md-inline"> Theme</span></button>
|
||||
{% endif %}
|
||||
<button id="sort-year" type="button" class="btn btn-outline-primary mb-2"
|
||||
data-sort-attribute="year"><i class="ri-calendar-line"></i><span class="d-none d-md-inline"> Year</span></button>
|
||||
{% if not use_pagination %}
|
||||
<button id="sort-minifigure" type="button" class="btn btn-outline-primary mb-2"
|
||||
data-sort-attribute="minifigures" data-sort-desc="true"><i class="ri-group-line"></i><span class="d-none d-xl-inline"> Figures</span></button>
|
||||
{% endif %}
|
||||
<button id="sort-parts" type="button" class="btn btn-outline-primary mb-2"
|
||||
data-sort-attribute="parts" data-sort-desc="true"><i class="ri-shapes-line"></i><span class="d-none d-xl-inline"> Parts</span></button>
|
||||
{% if not use_pagination %}
|
||||
{% if not config['HIDE_TABLE_MISSING_PARTS'] %}
|
||||
<button id="sort-missing" type="button" class="btn btn-outline-primary mb-2"
|
||||
data-sort-attribute="missing" data-sort-desc="true"><i class="ri-question-line"></i><span class="d-none d-xl-inline"> Missing</span></button>
|
||||
@@ -31,7 +26,6 @@
|
||||
data-sort-attribute="purchase-date" data-sort-desc="true"><i class="ri-calendar-line"></i><span class="d-none d-xl-inline"> Date</span></button>
|
||||
<button id="sort-purchase-price" type="button" class="btn btn-outline-primary mb-2"
|
||||
data-sort-attribute="purchase-price" data-sort-desc="true"><i class="ri-wallet-3-line"></i><span class="d-none d-xl-inline"> Price</span></button>
|
||||
{% endif %}
|
||||
<button id="sort-clear" type="button" class="btn btn-outline-dark mb-2"
|
||||
data-sort-clear="true"><i class="ri-close-circle-line"></i><span class="d-none d-xl-inline"> Clear</span></button>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{% block title %} - All sets{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% if collection | length %}
|
||||
{% if collection | length or use_pagination %}
|
||||
<div class="container-fluid">
|
||||
<div class="row row-cols-lg-auto g-1 justify-content-center align-items-center pb-2">
|
||||
<div class="col-12 flex-grow-1">
|
||||
@@ -21,7 +21,6 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% if not use_pagination %}
|
||||
<div class="col-12">
|
||||
<div class="input-group">
|
||||
<button class="btn btn-outline-primary" type="button" data-bs-toggle="collapse" data-bs-target="#grid-filter" aria-expanded="{% if config['SHOW_GRID_FILTERS'] %}true{% else %}false{% endif %}" aria-controls="grid-filter">
|
||||
@@ -29,12 +28,9 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% include 'set/sort.html' %}
|
||||
{% if not use_pagination %}
|
||||
{% include 'set/filter.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% if use_pagination %}
|
||||
<!-- PAGINATION MODE -->
|
||||
@@ -144,9 +140,13 @@
|
||||
<!-- Results Info -->
|
||||
<div class="text-center mt-3">
|
||||
<small class="text-muted">
|
||||
Showing {{ ((pagination.page - 1) * pagination.per_page + 1) }} to
|
||||
{{ [pagination.page * pagination.per_page, pagination.total_count] | min }}
|
||||
of {{ pagination.total_count }} sets
|
||||
{% if pagination.total_count > 0 %}
|
||||
Showing {{ ((pagination.page - 1) * pagination.per_page + 1) }} to
|
||||
{{ [pagination.page * pagination.per_page, pagination.total_count] | min }}
|
||||
of {{ pagination.total_count }} sets
|
||||
{% else %}
|
||||
No sets found
|
||||
{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user