mirror of
https://gitea.baerentsen.space/FrederikBaerentsen/BrickTracker.git
synced 2026-04-24 23:48:37 -05:00
feat: added filters to /parts, /problems, /minifigures
This commit is contained in:
@@ -43,6 +43,19 @@ class BrickMinifigureList(BrickRecordList[BrickMinifigure]):
|
||||
|
||||
return self
|
||||
|
||||
# Load all minifigures with problems filter
|
||||
def all_filtered(self, /, problems_filter: str = 'all', theme_id: str = 'all', year: str = 'all') -> Self:
|
||||
context = {}
|
||||
if problems_filter and problems_filter != 'all':
|
||||
context['problems_filter'] = problems_filter
|
||||
if theme_id and theme_id != 'all':
|
||||
context['theme_id'] = theme_id
|
||||
if year and year != 'all':
|
||||
context['year'] = year
|
||||
|
||||
self.list(override_query=self.all_query, **context)
|
||||
return self
|
||||
|
||||
# Load all minifigures by owner
|
||||
def all_by_owner(self, owner_id: str | None = None, /) -> Self:
|
||||
# Save the owner_id parameter
|
||||
@@ -53,10 +66,31 @@ class BrickMinifigureList(BrickRecordList[BrickMinifigure]):
|
||||
|
||||
return self
|
||||
|
||||
# Load all minifigures by owner with problems filter
|
||||
def all_by_owner_filtered(self, /, owner_id: str | None = None, problems_filter: str = 'all', theme_id: str = 'all', year: str = 'all') -> Self:
|
||||
# Save the owner_id parameter
|
||||
self.fields.owner_id = owner_id
|
||||
|
||||
context = {}
|
||||
if problems_filter and problems_filter != 'all':
|
||||
context['problems_filter'] = problems_filter
|
||||
if theme_id and theme_id != 'all':
|
||||
context['theme_id'] = theme_id
|
||||
if year and year != 'all':
|
||||
context['year'] = year
|
||||
|
||||
# Load the minifigures from the database
|
||||
self.list(override_query=self.all_by_owner_query, **context)
|
||||
|
||||
return self
|
||||
|
||||
# Load minifigures with pagination support
|
||||
def all_filtered_paginated(
|
||||
self,
|
||||
owner_id: str | None = None,
|
||||
problems_filter: str = 'all',
|
||||
theme_id: str = 'all',
|
||||
year: str = 'all',
|
||||
search_query: str | None = None,
|
||||
page: int = 1,
|
||||
per_page: int = 50,
|
||||
@@ -74,6 +108,15 @@ class BrickMinifigureList(BrickRecordList[BrickMinifigure]):
|
||||
if search_query:
|
||||
filter_context['search_query'] = search_query
|
||||
|
||||
if problems_filter and problems_filter != 'all':
|
||||
filter_context['problems_filter'] = problems_filter
|
||||
|
||||
if theme_id and theme_id != 'all':
|
||||
filter_context['theme_id'] = theme_id
|
||||
|
||||
if year and year != 'all':
|
||||
filter_context['year'] = year
|
||||
|
||||
# Field mapping for sorting
|
||||
field_mapping = {
|
||||
'name': '"rebrickable_minifigures"."name"',
|
||||
|
||||
@@ -57,8 +57,8 @@ class BrickPartList(BrickRecordList[BrickPart]):
|
||||
|
||||
return self
|
||||
|
||||
# Load all parts with filters (owner and/or color)
|
||||
def all_filtered(self, owner_id: str | None = None, color_id: str | None = None, /) -> Self:
|
||||
# Load all parts with filters (owner, color, theme, year)
|
||||
def all_filtered(self, owner_id: str | None = None, color_id: str | None = None, theme_id: str | None = None, year: str | None = None, /) -> Self:
|
||||
# Save the filter parameters
|
||||
if owner_id is not None:
|
||||
self.fields.owner_id = owner_id
|
||||
@@ -75,6 +75,10 @@ class BrickPartList(BrickRecordList[BrickPart]):
|
||||
context = {}
|
||||
if current_app.config.get('SKIP_SPARE_PARTS', False):
|
||||
context['skip_spare_parts'] = True
|
||||
if theme_id and theme_id != 'all':
|
||||
context['theme_id'] = theme_id
|
||||
if year and year != 'all':
|
||||
context['year'] = year
|
||||
|
||||
# Load the parts from the database
|
||||
self.list(override_query=query, **context)
|
||||
@@ -86,6 +90,8 @@ class BrickPartList(BrickRecordList[BrickPart]):
|
||||
self,
|
||||
owner_id: str | None = None,
|
||||
color_id: str | None = None,
|
||||
theme_id: str | None = None,
|
||||
year: str | None = None,
|
||||
search_query: str | None = None,
|
||||
page: int = 1,
|
||||
per_page: int = 50,
|
||||
@@ -102,6 +108,10 @@ class BrickPartList(BrickRecordList[BrickPart]):
|
||||
|
||||
if color_id and color_id != 'all':
|
||||
filter_context['color_id'] = color_id
|
||||
if theme_id and theme_id != 'all':
|
||||
filter_context['theme_id'] = theme_id
|
||||
if year and year != 'all':
|
||||
filter_context['year'] = year
|
||||
if search_query:
|
||||
filter_context['search_query'] = search_query
|
||||
if current_app.config.get('SKIP_SPARE_PARTS', False):
|
||||
@@ -238,7 +248,7 @@ class BrickPartList(BrickRecordList[BrickPart]):
|
||||
|
||||
return self
|
||||
|
||||
def problem_filtered(self, owner_id: str | None = None, color_id: str | None = None, /) -> Self:
|
||||
def problem_filtered(self, owner_id: str | None = None, color_id: str | None = None, theme_id: str | None = None, year: str | None = None, /) -> Self:
|
||||
# Save the filter parameters for client-side filtering
|
||||
if owner_id is not None:
|
||||
self.fields.owner_id = owner_id
|
||||
@@ -251,6 +261,10 @@ class BrickPartList(BrickRecordList[BrickPart]):
|
||||
context['owner_id'] = owner_id
|
||||
if color_id and color_id != 'all':
|
||||
context['color_id'] = color_id
|
||||
if theme_id and theme_id != 'all':
|
||||
context['theme_id'] = theme_id
|
||||
if year and year != 'all':
|
||||
context['year'] = year
|
||||
if current_app.config.get('SKIP_SPARE_PARTS', False):
|
||||
context['skip_spare_parts'] = True
|
||||
|
||||
@@ -263,6 +277,8 @@ class BrickPartList(BrickRecordList[BrickPart]):
|
||||
self,
|
||||
owner_id: str | None = None,
|
||||
color_id: str | None = None,
|
||||
theme_id: str | None = None,
|
||||
year: str | None = None,
|
||||
search_query: str | None = None,
|
||||
page: int = 1,
|
||||
per_page: int = 50,
|
||||
@@ -275,6 +291,10 @@ class BrickPartList(BrickRecordList[BrickPart]):
|
||||
filter_context['owner_id'] = owner_id
|
||||
if color_id and color_id != 'all':
|
||||
filter_context['color_id'] = color_id
|
||||
if theme_id and theme_id != 'all':
|
||||
filter_context['theme_id'] = theme_id
|
||||
if year and year != 'all':
|
||||
filter_context['year'] = year
|
||||
if search_query:
|
||||
filter_context['search_query'] = search_query
|
||||
if current_app.config.get('SKIP_SPARE_PARTS', False):
|
||||
|
||||
@@ -28,6 +28,8 @@ ON "bricktracker_minifigures"."figure" IS NOT DISTINCT FROM "rebrickable_minifig
|
||||
|
||||
{% block group %}{% endblock %}
|
||||
|
||||
{% block having %}{% endblock %}
|
||||
|
||||
{% if order %}
|
||||
ORDER BY {{ order }}
|
||||
{% endif %}
|
||||
|
||||
@@ -17,6 +17,14 @@ IFNULL(COUNT("bricktracker_minifigures"."id"), 0) AS "total_sets"
|
||||
{% endblock %}
|
||||
|
||||
{% block join %}
|
||||
{% if theme_id or year %}
|
||||
-- Join with sets for theme/year filtering
|
||||
INNER JOIN "bricktracker_sets" AS "filter_sets"
|
||||
ON "bricktracker_minifigures"."id" IS NOT DISTINCT FROM "filter_sets"."id"
|
||||
INNER JOIN "rebrickable_sets" AS "filter_rs"
|
||||
ON "filter_sets"."set" IS NOT DISTINCT FROM "filter_rs"."set"
|
||||
{% endif %}
|
||||
|
||||
-- LEFT JOIN + SELECT to avoid messing the total
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
@@ -35,8 +43,28 @@ AND "rebrickable_minifigures"."figure" IS NOT DISTINCT FROM "problem_join"."figu
|
||||
{% endblock %}
|
||||
|
||||
{% block where %}
|
||||
WHERE 1=1
|
||||
{% if theme_id and theme_id != 'all' %}
|
||||
AND "filter_rs"."theme_id" = {{ theme_id }}
|
||||
{% endif %}
|
||||
{% if year and year != 'all' %}
|
||||
AND "filter_rs"."year" = {{ year }}
|
||||
{% endif %}
|
||||
{% if search_query %}
|
||||
WHERE (LOWER("rebrickable_minifigures"."name") LIKE LOWER('%{{ search_query }}%'))
|
||||
AND (LOWER("rebrickable_minifigures"."name") LIKE LOWER('%{{ search_query }}%'))
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block having %}
|
||||
{% if problems_filter %}
|
||||
HAVING 1=1
|
||||
{% if problems_filter == 'missing' %}
|
||||
AND SUM(IFNULL("problem_join"."total_missing", 0)) > 0
|
||||
{% elif problems_filter == 'damaged' %}
|
||||
AND SUM(IFNULL("problem_join"."total_damaged", 0)) > 0
|
||||
{% elif problems_filter == 'both' %}
|
||||
AND SUM(IFNULL("problem_join"."total_missing", 0)) > 0 AND SUM(IFNULL("problem_join"."total_damaged", 0)) > 0
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@ COUNT("bricktracker_minifigures"."id") AS "total_sets"
|
||||
INNER JOIN "bricktracker_sets"
|
||||
ON "bricktracker_minifigures"."id" IS NOT DISTINCT FROM "bricktracker_sets"."id"
|
||||
|
||||
-- Join with rebrickable sets for theme/year filtering
|
||||
INNER JOIN "rebrickable_sets"
|
||||
ON "bricktracker_sets"."set" IS NOT DISTINCT FROM "rebrickable_sets"."set"
|
||||
|
||||
-- Left join with set owners (using dynamic columns)
|
||||
LEFT JOIN "bricktracker_set_owners"
|
||||
ON "bricktracker_sets"."id" IS NOT DISTINCT FROM "bricktracker_set_owners"."id"
|
||||
@@ -64,6 +68,12 @@ AND "rebrickable_minifigures"."figure" IS NOT DISTINCT FROM "problem_join"."figu
|
||||
{% if owner_id and owner_id != 'all' %}
|
||||
{% set _ = conditions.append('"bricktracker_set_owners"."owner_' ~ owner_id ~ '" = 1') %}
|
||||
{% endif %}
|
||||
{% if theme_id and theme_id != 'all' %}
|
||||
{% set _ = conditions.append('"rebrickable_sets"."theme_id" = ' ~ theme_id) %}
|
||||
{% endif %}
|
||||
{% if year and year != 'all' %}
|
||||
{% set _ = conditions.append('"rebrickable_sets"."year" = ' ~ year) %}
|
||||
{% endif %}
|
||||
{% if search_query %}
|
||||
{% set _ = conditions.append('(LOWER("rebrickable_minifigures"."name") LIKE LOWER(\'%' ~ search_query ~ '%\'))') %}
|
||||
{% endif %}
|
||||
@@ -75,4 +85,17 @@ WHERE {{ conditions | join(' AND ') }}
|
||||
{% block group %}
|
||||
GROUP BY
|
||||
"rebrickable_minifigures"."figure"
|
||||
{% endblock %}
|
||||
|
||||
{% block having %}
|
||||
{% if problems_filter %}
|
||||
HAVING 1=1
|
||||
{% if problems_filter == 'missing' %}
|
||||
AND SUM(IFNULL("problem_join"."total_missing", 0)) > 0
|
||||
{% elif problems_filter == 'damaged' %}
|
||||
AND SUM(IFNULL("problem_join"."total_damaged", 0)) > 0
|
||||
{% elif problems_filter == 'both' %}
|
||||
AND SUM(IFNULL("problem_join"."total_missing", 0)) > 0 AND SUM(IFNULL("problem_join"."total_damaged", 0)) > 0
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,16 @@
|
||||
-- Get distinct themes from minifigures' sets
|
||||
SELECT DISTINCT
|
||||
"rebrickable_sets"."theme_id",
|
||||
COUNT(DISTINCT "bricktracker_minifigures"."figure") as "minifigure_count"
|
||||
FROM "bricktracker_minifigures"
|
||||
INNER JOIN "bricktracker_sets"
|
||||
ON "bricktracker_minifigures"."id" IS NOT DISTINCT FROM "bricktracker_sets"."id"
|
||||
INNER JOIN "rebrickable_sets"
|
||||
ON "bricktracker_sets"."set" IS NOT DISTINCT FROM "rebrickable_sets"."set"
|
||||
{% if owner_id and owner_id != 'all' %}
|
||||
INNER JOIN "bricktracker_set_owners"
|
||||
ON "bricktracker_sets"."id" IS NOT DISTINCT FROM "bricktracker_set_owners"."id"
|
||||
WHERE "bricktracker_set_owners"."owner_{{ owner_id }}" = 1
|
||||
{% endif %}
|
||||
GROUP BY "rebrickable_sets"."theme_id"
|
||||
ORDER BY "rebrickable_sets"."theme_id" ASC
|
||||
@@ -0,0 +1,16 @@
|
||||
-- Get distinct years from minifigures' sets
|
||||
SELECT DISTINCT
|
||||
"rebrickable_sets"."year",
|
||||
COUNT(DISTINCT "bricktracker_minifigures"."figure") as "minifigure_count"
|
||||
FROM "bricktracker_minifigures"
|
||||
INNER JOIN "bricktracker_sets"
|
||||
ON "bricktracker_minifigures"."id" IS NOT DISTINCT FROM "bricktracker_sets"."id"
|
||||
INNER JOIN "rebrickable_sets"
|
||||
ON "bricktracker_sets"."set" IS NOT DISTINCT FROM "rebrickable_sets"."set"
|
||||
{% if owner_id and owner_id != 'all' %}
|
||||
INNER JOIN "bricktracker_set_owners"
|
||||
ON "bricktracker_sets"."id" IS NOT DISTINCT FROM "bricktracker_set_owners"."id"
|
||||
WHERE "bricktracker_set_owners"."owner_{{ owner_id }}" = 1
|
||||
{% endif %}
|
||||
GROUP BY "rebrickable_sets"."year"
|
||||
ORDER BY "rebrickable_sets"."year" DESC
|
||||
@@ -24,6 +24,13 @@ SUM(IFNULL("bricktracker_minifigures"."quantity", 0)) AS "total_minifigures"
|
||||
LEFT JOIN "bricktracker_minifigures"
|
||||
ON "bricktracker_parts"."id" IS NOT DISTINCT FROM "bricktracker_minifigures"."id"
|
||||
AND "bricktracker_parts"."figure" IS NOT DISTINCT FROM "bricktracker_minifigures"."figure"
|
||||
|
||||
{% if theme_id or year %}
|
||||
INNER JOIN "bricktracker_sets" AS "filter_sets"
|
||||
ON "bricktracker_parts"."id" IS NOT DISTINCT FROM "filter_sets"."id"
|
||||
INNER JOIN "rebrickable_sets" AS "filter_rs"
|
||||
ON "filter_sets"."set" IS NOT DISTINCT FROM "filter_rs"."set"
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block where %}
|
||||
@@ -31,6 +38,12 @@ AND "bricktracker_parts"."figure" IS NOT DISTINCT FROM "bricktracker_minifigures
|
||||
{% if color_id and color_id != 'all' %}
|
||||
{% set _ = conditions.append('"bricktracker_parts"."color" = ' ~ color_id) %}
|
||||
{% endif %}
|
||||
{% if theme_id and theme_id != 'all' %}
|
||||
{% set _ = conditions.append('"filter_rs"."theme_id" = ' ~ theme_id) %}
|
||||
{% endif %}
|
||||
{% if year and year != 'all' %}
|
||||
{% set _ = conditions.append('"filter_rs"."year" = ' ~ year) %}
|
||||
{% endif %}
|
||||
{% if search_query %}
|
||||
{% set search_condition = '(LOWER("rebrickable_parts"."name") LIKE LOWER(\'%' ~ search_query ~ '%\') OR LOWER("rebrickable_parts"."color_name") LIKE LOWER(\'%' ~ search_query ~ '%\') OR LOWER("bricktracker_parts"."part") LIKE LOWER(\'%' ~ search_query ~ '%\'))' %}
|
||||
{% set _ = conditions.append(search_condition) %}
|
||||
|
||||
@@ -45,6 +45,10 @@ SUM(IFNULL("bricktracker_minifigures"."quantity", 0)) AS "total_minifigures"
|
||||
INNER JOIN "bricktracker_sets"
|
||||
ON "bricktracker_parts"."id" IS NOT DISTINCT FROM "bricktracker_sets"."id"
|
||||
|
||||
-- Join with rebrickable sets for theme/year filtering
|
||||
INNER JOIN "rebrickable_sets"
|
||||
ON "bricktracker_sets"."set" IS NOT DISTINCT FROM "rebrickable_sets"."set"
|
||||
|
||||
-- Left join with set owners (using dynamic columns)
|
||||
LEFT JOIN "bricktracker_set_owners"
|
||||
ON "bricktracker_sets"."id" IS NOT DISTINCT FROM "bricktracker_set_owners"."id"
|
||||
@@ -65,6 +69,12 @@ AND "bricktracker_parts"."figure" IS NOT DISTINCT FROM "bricktracker_minifigures
|
||||
{% if color_id and color_id != 'all' %}
|
||||
{% set _ = conditions.append('"bricktracker_parts"."color" = ' ~ color_id) %}
|
||||
{% endif %}
|
||||
{% if theme_id and theme_id != 'all' %}
|
||||
{% set _ = conditions.append('"rebrickable_sets"."theme_id" = ' ~ theme_id) %}
|
||||
{% endif %}
|
||||
{% if year and year != 'all' %}
|
||||
{% set _ = conditions.append('"rebrickable_sets"."year" = ' ~ year) %}
|
||||
{% endif %}
|
||||
{% if search_query %}
|
||||
{% set search_condition = '(LOWER("rebrickable_parts"."name") LIKE LOWER(\'%' ~ search_query ~ '%\') OR LOWER("rebrickable_parts"."color_name") LIKE LOWER(\'%' ~ search_query ~ '%\') OR LOWER("bricktracker_parts"."part") LIKE LOWER(\'%' ~ search_query ~ '%\'))' %}
|
||||
{% set _ = conditions.append(search_condition) %}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
-- Get distinct themes from parts' sets
|
||||
SELECT DISTINCT
|
||||
"rebrickable_sets"."theme_id",
|
||||
COUNT(DISTINCT "bricktracker_parts"."part") as "part_count"
|
||||
FROM "bricktracker_parts"
|
||||
INNER JOIN "bricktracker_sets"
|
||||
ON "bricktracker_parts"."id" IS NOT DISTINCT FROM "bricktracker_sets"."id"
|
||||
INNER JOIN "rebrickable_sets"
|
||||
ON "bricktracker_sets"."set" IS NOT DISTINCT FROM "rebrickable_sets"."set"
|
||||
{% if owner_id and owner_id != 'all' %}
|
||||
INNER JOIN "bricktracker_set_owners"
|
||||
ON "bricktracker_sets"."id" IS NOT DISTINCT FROM "bricktracker_set_owners"."id"
|
||||
WHERE "bricktracker_set_owners"."owner_{{ owner_id }}" = 1
|
||||
{% endif %}
|
||||
GROUP BY "rebrickable_sets"."theme_id"
|
||||
ORDER BY "rebrickable_sets"."theme_id" ASC
|
||||
@@ -0,0 +1,19 @@
|
||||
-- Get distinct themes from problem parts' sets
|
||||
SELECT DISTINCT
|
||||
"rebrickable_sets"."theme_id",
|
||||
COUNT(DISTINCT "bricktracker_parts"."part") as "part_count"
|
||||
FROM "bricktracker_parts"
|
||||
INNER JOIN "bricktracker_sets"
|
||||
ON "bricktracker_parts"."id" IS NOT DISTINCT FROM "bricktracker_sets"."id"
|
||||
INNER JOIN "rebrickable_sets"
|
||||
ON "bricktracker_sets"."set" IS NOT DISTINCT FROM "rebrickable_sets"."set"
|
||||
{% if owner_id and owner_id != 'all' %}
|
||||
INNER JOIN "bricktracker_set_owners"
|
||||
ON "bricktracker_sets"."id" IS NOT DISTINCT FROM "bricktracker_set_owners"."id"
|
||||
{% endif %}
|
||||
WHERE ("bricktracker_parts"."missing" > 0 OR "bricktracker_parts"."damaged" > 0)
|
||||
{% if owner_id and owner_id != 'all' %}
|
||||
AND "bricktracker_set_owners"."owner_{{ owner_id }}" = 1
|
||||
{% endif %}
|
||||
GROUP BY "rebrickable_sets"."theme_id"
|
||||
ORDER BY "rebrickable_sets"."theme_id" ASC
|
||||
@@ -0,0 +1,16 @@
|
||||
-- Get distinct years from parts' sets
|
||||
SELECT DISTINCT
|
||||
"rebrickable_sets"."year",
|
||||
COUNT(DISTINCT "bricktracker_parts"."part") as "part_count"
|
||||
FROM "bricktracker_parts"
|
||||
INNER JOIN "bricktracker_sets"
|
||||
ON "bricktracker_parts"."id" IS NOT DISTINCT FROM "bricktracker_sets"."id"
|
||||
INNER JOIN "rebrickable_sets"
|
||||
ON "bricktracker_sets"."set" IS NOT DISTINCT FROM "rebrickable_sets"."set"
|
||||
{% if owner_id and owner_id != 'all' %}
|
||||
INNER JOIN "bricktracker_set_owners"
|
||||
ON "bricktracker_sets"."id" IS NOT DISTINCT FROM "bricktracker_set_owners"."id"
|
||||
WHERE "bricktracker_set_owners"."owner_{{ owner_id }}" = 1
|
||||
{% endif %}
|
||||
GROUP BY "rebrickable_sets"."year"
|
||||
ORDER BY "rebrickable_sets"."year" DESC
|
||||
@@ -0,0 +1,19 @@
|
||||
-- Get distinct years from problem parts' sets
|
||||
SELECT DISTINCT
|
||||
"rebrickable_sets"."year",
|
||||
COUNT(DISTINCT "bricktracker_parts"."part") as "part_count"
|
||||
FROM "bricktracker_parts"
|
||||
INNER JOIN "bricktracker_sets"
|
||||
ON "bricktracker_parts"."id" IS NOT DISTINCT FROM "bricktracker_sets"."id"
|
||||
INNER JOIN "rebrickable_sets"
|
||||
ON "bricktracker_sets"."set" IS NOT DISTINCT FROM "rebrickable_sets"."set"
|
||||
{% if owner_id and owner_id != 'all' %}
|
||||
INNER JOIN "bricktracker_set_owners"
|
||||
ON "bricktracker_sets"."id" IS NOT DISTINCT FROM "bricktracker_set_owners"."id"
|
||||
{% endif %}
|
||||
WHERE ("bricktracker_parts"."missing" > 0 OR "bricktracker_parts"."damaged" > 0)
|
||||
{% if owner_id and owner_id != 'all' %}
|
||||
AND "bricktracker_set_owners"."owner_{{ owner_id }}" = 1
|
||||
{% endif %}
|
||||
GROUP BY "rebrickable_sets"."year"
|
||||
ORDER BY "rebrickable_sets"."year" DESC
|
||||
@@ -16,6 +16,9 @@ minifigure_page = Blueprint('minifigure', __name__, url_prefix='/minifigures')
|
||||
def list() -> str:
|
||||
# Get filter parameters from request
|
||||
owner_id = request.args.get('owner', 'all')
|
||||
problems_filter = request.args.get('problems', 'all')
|
||||
theme_id = request.args.get('theme', 'all')
|
||||
year = request.args.get('year', 'all')
|
||||
search_query, sort_field, sort_order, page = get_request_params()
|
||||
|
||||
# Get pagination configuration
|
||||
@@ -26,6 +29,9 @@ def list() -> str:
|
||||
# PAGINATION MODE - Server-side pagination with search
|
||||
minifigures, total_count = BrickMinifigureList().all_filtered_paginated(
|
||||
owner_id=owner_id,
|
||||
problems_filter=problems_filter,
|
||||
theme_id=theme_id,
|
||||
year=year,
|
||||
search_query=search_query,
|
||||
page=page,
|
||||
per_page=per_page,
|
||||
@@ -37,19 +43,45 @@ def list() -> str:
|
||||
else:
|
||||
# ORIGINAL MODE - Single page with all data for client-side search
|
||||
if owner_id == 'all' or owner_id is None or owner_id == '':
|
||||
minifigures = BrickMinifigureList().all()
|
||||
minifigures = BrickMinifigureList().all_filtered(problems_filter=problems_filter, theme_id=theme_id, year=year)
|
||||
else:
|
||||
minifigures = BrickMinifigureList().all_by_owner(owner_id)
|
||||
minifigures = BrickMinifigureList().all_by_owner_filtered(owner_id=owner_id, problems_filter=problems_filter, theme_id=theme_id, year=year)
|
||||
|
||||
pagination_context = None
|
||||
|
||||
# Get list of owners for filter dropdown
|
||||
owners = BrickSetOwnerList.list()
|
||||
|
||||
# Prepare context for dependent filters
|
||||
filter_context = {}
|
||||
if owner_id != 'all' and owner_id:
|
||||
filter_context['owner_id'] = owner_id
|
||||
|
||||
# Get list of themes for filter dropdown
|
||||
from ..theme_list import BrickThemeList
|
||||
from ..sql import BrickSQL
|
||||
theme_list = BrickThemeList()
|
||||
themes_data = BrickSQL().fetchall('minifigure/themes/list', **filter_context)
|
||||
themes = []
|
||||
for theme_data in themes_data:
|
||||
theme = theme_list.get(theme_data['theme_id'])
|
||||
themes.append({
|
||||
'theme_id': theme_data['theme_id'],
|
||||
'theme_name': theme.name if theme else f"Theme {theme_data['theme_id']}"
|
||||
})
|
||||
|
||||
# Get list of years for filter dropdown
|
||||
years = BrickSQL().fetchall('minifigure/years/list', **filter_context)
|
||||
|
||||
template_context = {
|
||||
'table_collection': minifigures,
|
||||
'owners': owners,
|
||||
'selected_owner': owner_id,
|
||||
'selected_problems': problems_filter,
|
||||
'themes': themes,
|
||||
'selected_theme': theme_id,
|
||||
'years': years,
|
||||
'selected_year': year,
|
||||
'search_query': search_query,
|
||||
'use_pagination': use_pagination,
|
||||
'current_sort': sort_field,
|
||||
|
||||
+60
-15
@@ -19,6 +19,8 @@ def list() -> str:
|
||||
# Get filter parameters from request
|
||||
owner_id = request.args.get('owner', 'all')
|
||||
color_id = request.args.get('color', 'all')
|
||||
theme_id = request.args.get('theme', 'all')
|
||||
year = request.args.get('year', 'all')
|
||||
search_query, sort_field, sort_order, page = get_request_params()
|
||||
|
||||
# Get pagination configuration
|
||||
@@ -30,6 +32,8 @@ def list() -> str:
|
||||
parts, total_count = BrickPartList().all_filtered_paginated(
|
||||
owner_id=owner_id,
|
||||
color_id=color_id,
|
||||
theme_id=theme_id,
|
||||
year=year,
|
||||
search_query=search_query,
|
||||
page=page,
|
||||
per_page=per_page,
|
||||
@@ -40,19 +44,34 @@ def list() -> str:
|
||||
pagination_context = build_pagination_context(page, per_page, total_count, is_mobile)
|
||||
else:
|
||||
# ORIGINAL MODE - Single page with all data for client-side search
|
||||
parts = BrickPartList().all_filtered(owner_id, color_id)
|
||||
parts = BrickPartList().all_filtered(owner_id, color_id, theme_id, year)
|
||||
pagination_context = None
|
||||
|
||||
# Get list of owners for filter dropdown
|
||||
owners = BrickSetOwnerList.list()
|
||||
|
||||
# Get list of colors for filter dropdown
|
||||
# Prepare context for color query (filter by owner if selected)
|
||||
color_context = {}
|
||||
# Prepare context for dependent filters
|
||||
filter_context = {}
|
||||
if owner_id != 'all' and owner_id:
|
||||
color_context['owner_id'] = owner_id
|
||||
filter_context['owner_id'] = owner_id
|
||||
|
||||
colors = BrickSQL().fetchall('part/colors/list', **color_context)
|
||||
# Get list of colors for filter dropdown
|
||||
colors = BrickSQL().fetchall('part/colors/list', **filter_context)
|
||||
|
||||
# Get list of themes for filter dropdown
|
||||
from ..theme_list import BrickThemeList
|
||||
theme_list = BrickThemeList()
|
||||
themes_data = BrickSQL().fetchall('part/themes/list', **filter_context)
|
||||
themes = []
|
||||
for theme_data in themes_data:
|
||||
theme = theme_list.get(theme_data['theme_id'])
|
||||
themes.append({
|
||||
'theme_id': theme_data['theme_id'],
|
||||
'theme_name': theme.name if theme else f"Theme {theme_data['theme_id']}"
|
||||
})
|
||||
|
||||
# Get list of years for filter dropdown
|
||||
years = BrickSQL().fetchall('part/years/list', **filter_context)
|
||||
|
||||
template_context = {
|
||||
'table_collection': parts,
|
||||
@@ -60,6 +79,10 @@ def list() -> str:
|
||||
'selected_owner': owner_id,
|
||||
'colors': colors,
|
||||
'selected_color': color_id,
|
||||
'themes': themes,
|
||||
'selected_theme': theme_id,
|
||||
'years': years,
|
||||
'selected_year': year,
|
||||
'search_query': search_query,
|
||||
'use_pagination': use_pagination,
|
||||
'current_sort': sort_field,
|
||||
@@ -80,6 +103,8 @@ def problem() -> str:
|
||||
# Get filter parameters from request
|
||||
owner_id = request.args.get('owner', 'all')
|
||||
color_id = request.args.get('color', 'all')
|
||||
theme_id = request.args.get('theme', 'all')
|
||||
year = request.args.get('year', 'all')
|
||||
search_query, sort_field, sort_order, page = get_request_params()
|
||||
|
||||
# Get pagination configuration
|
||||
@@ -91,6 +116,8 @@ def problem() -> str:
|
||||
parts, total_count = BrickPartList().problem_paginated(
|
||||
owner_id=owner_id,
|
||||
color_id=color_id,
|
||||
theme_id=theme_id,
|
||||
year=year,
|
||||
search_query=search_query,
|
||||
page=page,
|
||||
per_page=per_page,
|
||||
@@ -101,20 +128,34 @@ def problem() -> str:
|
||||
pagination_context = build_pagination_context(page, per_page, total_count, is_mobile)
|
||||
else:
|
||||
# ORIGINAL MODE - Single page with all data for client-side search
|
||||
parts = BrickPartList().problem_filtered(owner_id, color_id)
|
||||
parts = BrickPartList().problem_filtered(owner_id, color_id, theme_id, year)
|
||||
pagination_context = None
|
||||
|
||||
# Get list of owners for filter dropdown
|
||||
owners = BrickSetOwnerList.list()
|
||||
|
||||
# Get list of colors for filter dropdown
|
||||
# Prepare context for color query (filter by owner if selected)
|
||||
color_context = {}
|
||||
if owner_id != 'all':
|
||||
color_context['owner_id'] = owner_id
|
||||
# Prepare context for dependent filters
|
||||
filter_context = {}
|
||||
if owner_id != 'all' and owner_id:
|
||||
filter_context['owner_id'] = owner_id
|
||||
|
||||
# Get colors from problem parts (following same pattern as parts page)
|
||||
colors = BrickSQL().fetchall('part/colors/list_problem', **color_context)
|
||||
# Get list of colors for filter dropdown (problem parts only)
|
||||
colors = BrickSQL().fetchall('part/colors/list_problem', **filter_context)
|
||||
|
||||
# Get list of themes for filter dropdown (problem parts only)
|
||||
from ..theme_list import BrickThemeList
|
||||
theme_list = BrickThemeList()
|
||||
themes_data = BrickSQL().fetchall('part/themes/list_problem', **filter_context)
|
||||
themes = []
|
||||
for theme_data in themes_data:
|
||||
theme = theme_list.get(theme_data['theme_id'])
|
||||
themes.append({
|
||||
'theme_id': theme_data['theme_id'],
|
||||
'theme_name': theme.name if theme else f"Theme {theme_data['theme_id']}"
|
||||
})
|
||||
|
||||
# Get list of years for filter dropdown (problem parts only)
|
||||
years = BrickSQL().fetchall('part/years/list_problem', **filter_context)
|
||||
|
||||
return render_template(
|
||||
'problem.html',
|
||||
@@ -127,7 +168,11 @@ def problem() -> str:
|
||||
owners=owners,
|
||||
colors=colors,
|
||||
selected_owner=owner_id,
|
||||
selected_color=color_id
|
||||
selected_color=color_id,
|
||||
themes=themes,
|
||||
selected_theme=theme_id,
|
||||
years=years,
|
||||
selected_year=year
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -314,10 +314,13 @@ window.updateUrlParams = function(params, resetPage = true) {
|
||||
window.location.href = currentUrl.toString();
|
||||
};
|
||||
|
||||
// Shared filter application (supports owner and color filters)
|
||||
// Shared filter application (supports owner, color, theme, year, and problems filters)
|
||||
window.applyPageFilters = function(tableId) {
|
||||
const ownerSelect = document.getElementById('filter-owner');
|
||||
const colorSelect = document.getElementById('filter-color');
|
||||
const themeSelect = document.getElementById('filter-theme');
|
||||
const yearSelect = document.getElementById('filter-year');
|
||||
const problemsSelect = document.getElementById('filter-problems');
|
||||
const params = {};
|
||||
|
||||
// Handle owner filter
|
||||
@@ -330,6 +333,21 @@ window.applyPageFilters = function(tableId) {
|
||||
params.color = colorSelect.value;
|
||||
}
|
||||
|
||||
// Handle theme filter
|
||||
if (themeSelect) {
|
||||
params.theme = themeSelect.value;
|
||||
}
|
||||
|
||||
// Handle year filter
|
||||
if (yearSelect) {
|
||||
params.year = yearSelect.value;
|
||||
}
|
||||
|
||||
// Handle problems filter (for minifigures page)
|
||||
if (problemsSelect) {
|
||||
params.problems = problemsSelect.value;
|
||||
}
|
||||
|
||||
// Update URL with new parameters
|
||||
window.updateUrlParams(params, true);
|
||||
};
|
||||
|
||||
@@ -6,15 +6,51 @@ function isPaginationMode() {
|
||||
return tableElement && tableElement.getAttribute('data-table') === 'false';
|
||||
}
|
||||
|
||||
function filterByOwner() {
|
||||
const select = document.getElementById('filter-owner');
|
||||
const selectedOwner = select.value;
|
||||
function applyFilters() {
|
||||
const ownerSelect = document.getElementById('filter-owner');
|
||||
const problemsSelect = document.getElementById('filter-problems');
|
||||
const themeSelect = document.getElementById('filter-theme');
|
||||
const yearSelect = document.getElementById('filter-year');
|
||||
const currentUrl = new URL(window.location);
|
||||
|
||||
if (selectedOwner === 'all') {
|
||||
currentUrl.searchParams.delete('owner');
|
||||
} else {
|
||||
currentUrl.searchParams.set('owner', selectedOwner);
|
||||
// Apply owner filter
|
||||
if (ownerSelect) {
|
||||
const selectedOwner = ownerSelect.value;
|
||||
if (selectedOwner === 'all') {
|
||||
currentUrl.searchParams.delete('owner');
|
||||
} else {
|
||||
currentUrl.searchParams.set('owner', selectedOwner);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply problems filter
|
||||
if (problemsSelect) {
|
||||
const selectedProblems = problemsSelect.value;
|
||||
if (selectedProblems === 'all') {
|
||||
currentUrl.searchParams.delete('problems');
|
||||
} else {
|
||||
currentUrl.searchParams.set('problems', selectedProblems);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply theme filter
|
||||
if (themeSelect) {
|
||||
const selectedTheme = themeSelect.value;
|
||||
if (selectedTheme === 'all') {
|
||||
currentUrl.searchParams.delete('theme');
|
||||
} else {
|
||||
currentUrl.searchParams.set('theme', selectedTheme);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply year filter
|
||||
if (yearSelect) {
|
||||
const selectedYear = yearSelect.value;
|
||||
if (selectedYear === 'all') {
|
||||
currentUrl.searchParams.delete('year');
|
||||
} else {
|
||||
currentUrl.searchParams.set('year', selectedYear);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset to page 1 when filtering
|
||||
@@ -25,15 +61,25 @@ function filterByOwner() {
|
||||
window.location.href = currentUrl.toString();
|
||||
}
|
||||
|
||||
// Legacy function for compatibility
|
||||
function filterByOwner() {
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
// Initialize filter and sort states for minifigures page
|
||||
function initializeCollapsibleStates() {
|
||||
initializePageCollapsibleStates('minifigures');
|
||||
}
|
||||
|
||||
// Keep filters expanded after selection
|
||||
function filterByOwnerAndKeepOpen() {
|
||||
function applyFiltersAndKeepOpen() {
|
||||
preserveCollapsibleStateOnChange('table-filter', 'minifigures-filter-state');
|
||||
filterByOwner();
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
// Legacy function for compatibility
|
||||
function filterByOwnerAndKeepOpen() {
|
||||
applyFiltersAndKeepOpen();
|
||||
}
|
||||
|
||||
// Setup table search and sort functionality
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div id="table-filter" class="collapse {% if config['SHOW_GRID_FILTERS'] %}show{% endif %} row row-cols-lg-auto g-1 justify-content-center align-items-center">
|
||||
{% if owners | length %}
|
||||
<div class="col-12 flex-grow-1">
|
||||
<div class="col-12 col-md-6 col-lg-3 flex-grow-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ri-user-line"></i><span class="ms-1 d-none d-md-inline"> Owner</span></span>
|
||||
<select id="filter-owner" class="form-select" onchange="filterByOwnerAndKeepOpen()" autocomplete="off">
|
||||
<select id="filter-owner" class="form-select" onchange="applyFiltersAndKeepOpen()" autocomplete="off">
|
||||
<option value="all" {% if selected_owner == 'all' %}selected{% endif %}>All owners</option>
|
||||
{% for owner in owners %}
|
||||
<option value="{{ owner.fields.id }}" {% if selected_owner == owner.fields.id %}selected{% endif %}>{{ owner.fields.name }}</option>
|
||||
@@ -12,4 +12,41 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-12 col-md-6 col-lg-3 flex-grow-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ri-error-warning-line"></i><span class="ms-1 d-none d-md-inline"> Problems</span></span>
|
||||
<select id="filter-problems" class="form-select" onchange="applyFiltersAndKeepOpen()" autocomplete="off">
|
||||
<option value="all" {% if selected_problems == 'all' %}selected{% endif %}>All minifigures</option>
|
||||
<option value="missing" {% if selected_problems == 'missing' %}selected{% endif %}>With missing parts</option>
|
||||
<option value="damaged" {% if selected_problems == 'damaged' %}selected{% endif %}>With damaged parts</option>
|
||||
<option value="both" {% if selected_problems == 'both' %}selected{% endif %}>With missing and damaged parts</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% if themes | length %}
|
||||
<div class="col-12 col-md-6 col-lg-3 flex-grow-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ri-price-tag-3-line"></i><span class="ms-1 d-none d-md-inline"> Theme</span></span>
|
||||
<select id="filter-theme" class="form-select" onchange="applyFiltersAndKeepOpen()" autocomplete="off">
|
||||
<option value="all" {% if selected_theme == 'all' %}selected{% endif %}>All themes</option>
|
||||
{% for theme in themes %}
|
||||
<option value="{{ theme.theme_id }}" {% if selected_theme == theme.theme_id|string %}selected{% endif %}>{{ theme.theme_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if years | length %}
|
||||
<div class="col-12 col-md-6 col-lg-3 flex-grow-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ri-calendar-line"></i><span class="ms-1 d-none d-md-inline"> Year</span></span>
|
||||
<select id="filter-year" class="form-select" onchange="applyFiltersAndKeepOpen()" autocomplete="off">
|
||||
<option value="all" {% if selected_year == 'all' %}selected{% endif %}>All years</option>
|
||||
{% for year in years %}
|
||||
<option value="{{ year.year }}" {% if selected_year == year.year|string %}selected{% endif %}>{{ year.year }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -1,6 +1,6 @@
|
||||
<div id="table-filter" class="collapse {% if config['SHOW_GRID_FILTERS'] %}show{% endif %} row row-cols-lg-auto g-1 justify-content-center align-items-center">
|
||||
{% if owners | length %}
|
||||
<div class="col-12 col-md-6 flex-grow-1">
|
||||
<div class="col-12 col-md-6 col-lg-3 flex-grow-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ri-user-line"></i><span class="ms-1 d-none d-md-inline"> Owner</span></span>
|
||||
<select id="filter-owner" class="form-select" onchange="applyFiltersAndKeepOpen()" autocomplete="off">
|
||||
@@ -13,7 +13,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if colors | length %}
|
||||
<div class="col-12 col-md-6 flex-grow-1">
|
||||
<div class="col-12 col-md-6 col-lg-3 flex-grow-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ri-palette-line"></i><span class="ms-1 d-none d-md-inline"> Color</span></span>
|
||||
<select id="filter-color" class="form-select" onchange="applyFiltersAndKeepOpen()" autocomplete="off">
|
||||
@@ -27,4 +27,30 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if themes | length %}
|
||||
<div class="col-12 col-md-6 col-lg-3 flex-grow-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ri-price-tag-3-line"></i><span class="ms-1 d-none d-md-inline"> Theme</span></span>
|
||||
<select id="filter-theme" class="form-select" onchange="applyFiltersAndKeepOpen()" autocomplete="off">
|
||||
<option value="all" {% if selected_theme == 'all' %}selected{% endif %}>All themes</option>
|
||||
{% for theme in themes %}
|
||||
<option value="{{ theme.theme_id }}" {% if selected_theme == theme.theme_id|string %}selected{% endif %}>{{ theme.theme_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if years | length %}
|
||||
<div class="col-12 col-md-6 col-lg-3 flex-grow-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ri-calendar-line"></i><span class="ms-1 d-none d-md-inline"> Year</span></span>
|
||||
<select id="filter-year" class="form-select" onchange="applyFiltersAndKeepOpen()" autocomplete="off">
|
||||
<option value="all" {% if selected_year == 'all' %}selected{% endif %}>All years</option>
|
||||
{% for year in years %}
|
||||
<option value="{{ year.year }}" {% if selected_year == year.year|string %}selected{% endif %}>{{ year.year }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -1,6 +1,6 @@
|
||||
<div id="table-filter" class="collapse {% if config['SHOW_GRID_FILTERS'] %}show{% endif %} row row-cols-lg-auto g-1 justify-content-center align-items-center">
|
||||
{% if owners | length %}
|
||||
<div class="col-12 col-md-6 flex-grow-1">
|
||||
<div class="col-12 col-md-6 col-lg-3 flex-grow-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ri-user-line"></i><span class="ms-1 d-none d-md-inline"> Owner</span></span>
|
||||
<select id="filter-owner" class="form-select" onchange="applyFiltersAndKeepOpen()" autocomplete="off">
|
||||
@@ -13,7 +13,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if colors | length %}
|
||||
<div class="col-12 col-md-6 flex-grow-1">
|
||||
<div class="col-12 col-md-6 col-lg-3 flex-grow-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ri-palette-line"></i><span class="ms-1 d-none d-md-inline"> Color</span></span>
|
||||
<select id="filter-color" class="form-select" onchange="applyFiltersAndKeepOpen()" autocomplete="off">
|
||||
@@ -27,4 +27,30 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if themes | length %}
|
||||
<div class="col-12 col-md-6 col-lg-3 flex-grow-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ri-price-tag-3-line"></i><span class="ms-1 d-none d-md-inline"> Theme</span></span>
|
||||
<select id="filter-theme" class="form-select" onchange="applyFiltersAndKeepOpen()" autocomplete="off">
|
||||
<option value="all" {% if selected_theme == 'all' %}selected{% endif %}>All themes</option>
|
||||
{% for theme in themes %}
|
||||
<option value="{{ theme.theme_id }}" {% if selected_theme == theme.theme_id|string %}selected{% endif %}>{{ theme.theme_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if years | length %}
|
||||
<div class="col-12 col-md-6 col-lg-3 flex-grow-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ri-calendar-line"></i><span class="ms-1 d-none d-md-inline"> Year</span></span>
|
||||
<select id="filter-year" class="form-select" onchange="applyFiltersAndKeepOpen()" autocomplete="off">
|
||||
<option value="all" {% if selected_year == 'all' %}selected{% endif %}>All years</option>
|
||||
{% for year in years %}
|
||||
<option value="{{ year.year }}" {% if selected_year == year.year|string %}selected{% endif %}>{{ year.year }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
Reference in New Issue
Block a user