mirror of
https://gitea.baerentsen.space/FrederikBaerentsen/BrickTracker.git
synced 2026-05-05 06:32:51 -05:00
feat(admin): added options to order badges on sets and details page.
This commit is contained in:
+15
-1
@@ -481,6 +481,20 @@
|
||||
# BK_STATISTICS_DEFAULT_EXPANDED=false
|
||||
|
||||
# Optional: Enable dark mode by default
|
||||
# When true, the application starts in dark mode.
|
||||
# When true, the application starts in dark mode.
|
||||
# Default: false
|
||||
# BK_DARK_MODE=true
|
||||
|
||||
# Optional: Customize badge order for Grid view (set cards on /sets/)
|
||||
# Comma-separated list of badge keys in the order they should appear
|
||||
# Available badges: theme, tag, year, parts, instance_count, total_minifigures,
|
||||
# total_missing, total_damaged, owner, storage, purchase_date, purchase_location,
|
||||
# purchase_price, instructions, rebrickable, bricklink
|
||||
# Default: theme,year,parts,total_minifigures,owner
|
||||
# BK_BADGE_ORDER_GRID=theme,year,parts,total_minifigures,owner,storage
|
||||
|
||||
# Optional: Customize badge order for Detail view (individual set details page)
|
||||
# Comma-separated list of badge keys in the order they should appear
|
||||
# Use the same badge keys as BK_BADGE_ORDER_GRID
|
||||
# Default: theme,tag,year,parts,instance_count,total_minifigures,total_missing,total_damaged,owner,storage,purchase_date,purchase_location,purchase_price,instructions,rebrickable,bricklink
|
||||
# BK_BADGE_ORDER_DETAIL=theme,tag,year,parts,owner,storage,purchase_date,rebrickable,bricklink
|
||||
|
||||
+24
-9
@@ -17,15 +17,29 @@
|
||||
- BrickLink exports use proper BrickLink part numbers and color IDs when available
|
||||
- Filter support: All part exports accept owner, color, theme, and year query parameters
|
||||
- Format information displayed in UI for user guidance
|
||||
- **Database Integrity Check and Cleanup** (from 1.3.2)
|
||||
- **Badge Order Customization**
|
||||
- Added customizable badge ordering for set cards and detail pages
|
||||
- Separate configurations for Grid view (`/sets/` cards) and Detail view (individual set pages)
|
||||
- Configure via environment variables in `.env` file:
|
||||
- `BK_BADGE_ORDER_GRID`: Comma-separated badge keys for grid view (default: theme,year,parts,total_minifigures,owner)
|
||||
- `BK_BADGE_ORDER_DETAIL`: Comma-separated badge keys for detail view (default: all 16 badges)
|
||||
- Can also be configured via Live Settings page in admin panel under "Default Ordering & Formatting"
|
||||
- Changes apply immediately without restart when edited via admin panel
|
||||
- 16 available badge types: theme, tag, year, parts, instance_count, total_minifigures, total_missing, total_damaged, owner, storage, purchase_date, purchase_location, purchase_price, instructions, rebrickable, bricklink
|
||||
|
||||
|
||||
## 1.3.1
|
||||
|
||||
### New Functionality
|
||||
|
||||
- **Database Integrity Check and Cleanup**
|
||||
- Added database integrity scanner to detect orphaned records and foreign key violations
|
||||
- New "Check Database Integrity" button in admin panel scans for issues
|
||||
- Detects orphaned sets, parts, and parts with missing set references
|
||||
- Two-step cleanup process with Bootstrap modal confirmation
|
||||
- Warning prompts users to backup database before cleanup
|
||||
- Automatic cleanup removes all orphaned records in one operation
|
||||
- Cleanup removes all orphaned records in one operation
|
||||
- Detailed scan results show affected records with counts and descriptions
|
||||
- **Database Optimization** (from 1.3.2)
|
||||
- **Database Optimization**
|
||||
- Added "Optimize Database" button to re-create performance indexes
|
||||
- Safe to run after database imports or restores
|
||||
- Re-creates all indexes from migration #19 using `CREATE INDEX IF NOT EXISTS`
|
||||
@@ -35,19 +49,20 @@
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Fixed foreign key constraint errors during set imports** (from 1.3.1): Resolved `FOREIGN KEY constraint failed` errors when importing sets with parts and minifigures
|
||||
- **Fixed foreign key constraint errors during set imports**: Resolved `FOREIGN KEY constraint failed` errors when importing sets with parts and minifigures
|
||||
- Fixed insertion order in `bricktracker/part.py`: Parent records (`rebrickable_parts`) now inserted before child records (`bricktracker_parts`)
|
||||
- Fixed insertion order in `bricktracker/minifigure.py`: Parent records (`rebrickable_minifigures`) now inserted before child records (`bricktracker_minifigures`)
|
||||
- Ensures foreign key references are valid when SQLite checks constraints
|
||||
- **Fixed set metadata updates** (from 1.3.1): Owner, status, and tag checkboxes now properly persist changes on set details page
|
||||
|
||||
- **Fixed set metadata updates**: Owner, status, and tag checkboxes now properly persist changes on set details page
|
||||
- Fixed `update_set_state()` method to commit database transactions (was using deferred execution without commit)
|
||||
- All metadata updates (owner, status, tags, storage, purchase info) now work consistently
|
||||
- **Fixed nil image downloads** (from 1.3.1): Placeholder images for parts and minifigures without images now download correctly
|
||||
- **Fixed nil image downloads**: Placeholder images for parts and minifigures without images now download correctly
|
||||
- Removed early returns that prevented nil image downloads
|
||||
- Nil images now properly saved to configured folders (e.g., `/app/data/parts/nil.jpg`)
|
||||
- **Fixed error logging for missing files** (from 1.3.1): File not found errors now show actual configured folder paths instead of just URL paths
|
||||
- **Fixed error logging for missing files**: File not found errors now show actual configured folder paths instead of just URL paths
|
||||
- Added detailed logging showing both file path and configured folder for easier debugging
|
||||
- **Fixed minifigure filters in client-side pagination mode** (from 1.3.1): Owner and other filters now work correctly when server-side pagination is disabled
|
||||
- **Fixed minifigure filters in client-side pagination mode**: Owner and other filters now work correctly when server-side pagination is disabled
|
||||
- Aligned filter behavior with parts page (applies filters server-side, then loads filtered data for client-side search)
|
||||
|
||||
## 1.3
|
||||
|
||||
@@ -97,4 +97,6 @@ CONFIG: Final[list[dict[str, Any]]] = [
|
||||
{'n': 'STATISTICS_SHOW_CHARTS', 'd': True, 'c': bool},
|
||||
{'n': 'STATISTICS_DEFAULT_EXPANDED', 'd': True, 'c': bool},
|
||||
{'n': 'DARK_MODE', 'c': bool},
|
||||
{'n': 'BADGE_ORDER_GRID', 'd': ['theme', 'year', 'parts', 'total_minifigures', 'owner'], 'c': list},
|
||||
{'n': 'BADGE_ORDER_DETAIL', 'd': ['theme', 'tag', 'year', 'parts', 'instance_count', 'total_minifigures', 'total_missing', 'total_damaged', 'owner', 'storage', 'purchase_date', 'purchase_location', 'purchase_price', 'instructions', 'rebrickable', 'bricklink'], 'c': list},
|
||||
]
|
||||
|
||||
@@ -54,6 +54,9 @@ LIVE_CHANGEABLE_VARS: Final[List[str]] = [
|
||||
'BK_STATISTICS_SHOW_CHARTS',
|
||||
'BK_STATISTICS_DEFAULT_EXPANDED',
|
||||
'BK_DARK_MODE',
|
||||
# Badge order preferences
|
||||
'BK_BADGE_ORDER_GRID',
|
||||
'BK_BADGE_ORDER_DETAIL',
|
||||
# Default ordering and formatting
|
||||
'BK_INSTRUCTIONS_ALLOWED_EXTENSIONS',
|
||||
'BK_MINIFIGURES_DEFAULT_ORDER',
|
||||
@@ -179,8 +182,8 @@ class ConfigManager:
|
||||
|
||||
def _cast_value(self, var_name: str, value: Any) -> Any:
|
||||
"""Cast value to appropriate type based on variable name"""
|
||||
# List variables (admin sections) - Check this FIRST before boolean check
|
||||
if 'sections' in var_name.lower():
|
||||
# List variables (admin sections, badge order) - Check this FIRST before boolean check
|
||||
if any(keyword in var_name.lower() for keyword in ['sections', 'badge_order']):
|
||||
if isinstance(value, str):
|
||||
return [section.strip() for section in value.split(',') if section.strip()]
|
||||
elif isinstance(value, list):
|
||||
|
||||
@@ -560,6 +560,22 @@
|
||||
<h6 class="fw-bold text-primary border-bottom pb-1 mb-3 mt-4">Default Ordering & Formatting</h6>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<label for="BK_BADGE_ORDER_GRID" class="form-label">
|
||||
BK_BADGE_ORDER_GRID {{ config_badges('BK_BADGE_ORDER_GRID') }}
|
||||
<div class="text-muted small">Badge order for grid view (comma-separated badge keys)</div>
|
||||
</label>
|
||||
<input type="text" class="form-control config-text" id="BK_BADGE_ORDER_GRID" data-var="BK_BADGE_ORDER_GRID" {{ is_locked('BK_BADGE_ORDER_GRID') }}>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label for="BK_BADGE_ORDER_DETAIL" class="form-label">
|
||||
BK_BADGE_ORDER_DETAIL {{ config_badges('BK_BADGE_ORDER_DETAIL') }}
|
||||
<div class="text-muted small">Badge order for detail view (comma-separated badge keys)</div>
|
||||
</label>
|
||||
<input type="text" class="form-control config-text" id="BK_BADGE_ORDER_DETAIL" data-var="BK_BADGE_ORDER_DETAIL" {{ is_locked('BK_BADGE_ORDER_DETAIL') }}>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label for="BK_INSTRUCTIONS_ALLOWED_EXTENSIONS" class="form-label">
|
||||
BK_INSTRUCTIONS_ALLOWED_EXTENSIONS {{ config_badges('BK_INSTRUCTIONS_ALLOWED_EXTENSIONS') }}
|
||||
|
||||
@@ -218,3 +218,69 @@
|
||||
{% macro year(year, solo=false, last=false) %}
|
||||
{{ badge(check=year, solo=solo, last=last, color='secondary', icon='calendar-line', collapsible='Year:', text=year, alt='Year') }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_ordered_badges(item, brickset_tags, brickset_owners, brickset_storages, brickset_purchase_locations, solo=false, last=false, context='grid') %}
|
||||
{# Get badge order from config based on context (grid or detail) #}
|
||||
{% if context == 'detail' %}
|
||||
{% set badge_order = config.get('BADGE_ORDER_DETAIL', ['theme', 'tag', 'year', 'parts', 'instance_count', 'total_minifigures', 'total_missing', 'total_damaged', 'owner', 'storage', 'purchase_date', 'purchase_location', 'purchase_price', 'instructions', 'rebrickable', 'bricklink']) %}
|
||||
{% else %}
|
||||
{% set badge_order = config.get('BADGE_ORDER_GRID', ['theme', 'year', 'parts', 'total_minifigures', 'owner']) %}
|
||||
{% endif %}
|
||||
|
||||
{# Render each badge in the configured order #}
|
||||
{% for badge_key in badge_order %}
|
||||
{% if badge_key == 'theme' %}
|
||||
{{ theme(item.theme.name, solo=solo, last=last) }}
|
||||
{% elif badge_key == 'tag' %}
|
||||
{% for tag_item in brickset_tags %}
|
||||
{{ tag(item, tag_item, solo=solo, last=last) }}
|
||||
{% endfor %}
|
||||
{% elif badge_key == 'year' %}
|
||||
{% if not last %}
|
||||
{{ year(item.fields.year, solo=solo, last=last) }}
|
||||
{% endif %}
|
||||
{% elif badge_key == 'parts' %}
|
||||
{{ parts(item.fields.number_of_parts, solo=solo, last=last) }}
|
||||
{% elif badge_key == 'instance_count' %}
|
||||
{% if item.fields.instance_count is defined and item.fields.instance_count > 1 %}
|
||||
<span class="badge bg-primary"><i class="ri-stack-line"></i> {{ item.fields.instance_count }} copies</span>
|
||||
{% endif %}
|
||||
{% elif badge_key == 'total_minifigures' %}
|
||||
{{ total_minifigures(item.fields.total_minifigures, solo=solo, last=last) }}
|
||||
{% elif badge_key == 'total_missing' %}
|
||||
{{ total_missing(item.fields.total_missing, solo=solo, last=last) }}
|
||||
{% elif badge_key == 'total_damaged' %}
|
||||
{{ total_damaged(item.fields.total_damaged, solo=solo, last=last) }}
|
||||
{% elif badge_key == 'owner' %}
|
||||
{% for owner_item in brickset_owners %}
|
||||
{{ owner(item, owner_item, solo=solo, last=last) }}
|
||||
{% endfor %}
|
||||
{% elif badge_key == 'storage' %}
|
||||
{{ storage(item, brickset_storages, solo=solo, last=last) }}
|
||||
{% elif badge_key == 'purchase_date' %}
|
||||
{% if not last %}
|
||||
{{ purchase_date(item.purchase_date(), solo=solo, last=last, date_max_formatted=item.purchase_date_max_formatted()) }}
|
||||
{% endif %}
|
||||
{% elif badge_key == 'purchase_location' %}
|
||||
{% if not last %}
|
||||
{{ purchase_location(item, brickset_purchase_locations, solo=solo, last=last) }}
|
||||
{% endif %}
|
||||
{% elif badge_key == 'purchase_price' %}
|
||||
{% if not last %}
|
||||
{{ purchase_price(item.purchase_price(), solo=solo, last=last) }}
|
||||
{% endif %}
|
||||
{% elif badge_key == 'instructions' %}
|
||||
{% if not last and not solo %}
|
||||
{{ instructions(item, solo=solo, last=last) }}
|
||||
{% endif %}
|
||||
{% elif badge_key == 'rebrickable' %}
|
||||
{% if not last %}
|
||||
{{ rebrickable(item, solo=solo, last=last) }}
|
||||
{% endif %}
|
||||
{% elif badge_key == 'bricklink' %}
|
||||
{% if not last %}
|
||||
{{ bricklink(item, solo=solo, last=last) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
+2
-28
@@ -57,34 +57,8 @@
|
||||
{{ card.header(item, item.fields.name, solo=solo, identifier=item.fields.set) }}
|
||||
{{ card.image(item, solo=solo, last=last, caption=item.fields.name, alt=item.fields.set) }}
|
||||
<div class="card-body border-bottom-0 {% if not solo %}p-1{% endif %}"{% if current_viewing %} style="border-color: var(--bs-border-color) !important; border-width: 1px !important;"{% endif %}>
|
||||
{{ badge.theme(item.theme.name, solo=solo, last=last) }}
|
||||
{% for tag in brickset_tags %}
|
||||
{{ badge.tag(item, tag, solo=solo, last=last) }}
|
||||
{% endfor %}
|
||||
{% if not last %}
|
||||
{{ badge.year(item.fields.year, solo=solo, last=last) }}
|
||||
{% endif %}
|
||||
{{ badge.parts(item.fields.number_of_parts, solo=solo, last=last) }}
|
||||
{% if item.fields.instance_count is defined and item.fields.instance_count > 1 %}
|
||||
<span class="badge bg-primary"><i class="ri-stack-line"></i> {{ item.fields.instance_count }} copies</span>
|
||||
{% endif %}
|
||||
{{ badge.total_minifigures(item.fields.total_minifigures, solo=solo, last=last) }}
|
||||
{{ badge.total_missing(item.fields.total_missing, solo=solo, last=last) }}
|
||||
{{ badge.total_damaged(item.fields.total_damaged, solo=solo, last=last) }}
|
||||
{% for owner in brickset_owners %}
|
||||
{{ badge.owner(item, owner, solo=solo, last=last) }}
|
||||
{% endfor %}
|
||||
{{ badge.storage(item, brickset_storages, solo=solo, last=last) }}
|
||||
{% if not last %}
|
||||
{{ badge.purchase_date(item.purchase_date(), solo=solo, last=last, date_max_formatted=item.purchase_date_max_formatted()) }}
|
||||
{{ badge.purchase_location(item, brickset_purchase_locations, solo=solo, last=last) }}
|
||||
{{ badge.purchase_price(item.purchase_price(), solo=solo, last=last) }}
|
||||
{% if not solo %}
|
||||
{{ badge.instructions(item, solo=solo, last=last) }}
|
||||
{% endif %}
|
||||
{{ badge.rebrickable(item, solo=solo, last=last) }}
|
||||
{{ badge.bricklink(item, solo=solo, last=last) }}
|
||||
{% endif %}
|
||||
{# Render badges in configured order based on context (grid view vs detail view) #}
|
||||
{{ badge.render_ordered_badges(item, brickset_tags, brickset_owners, brickset_storages, brickset_purchase_locations, solo=solo, last=last, context='detail' if solo else 'grid') }}
|
||||
</div>
|
||||
{% if not tiny and brickset_statuses | length %}
|
||||
<ul class="list-group list-group-flush card-check border-bottom-0"{% if current_viewing %} style="border-color: var(--bs-border-color) !important; border-width: 1px !important;"{% endif %}>
|
||||
|
||||
Reference in New Issue
Block a user