feat(minifigures): individual minifigures can now be disabled using env var

This commit is contained in:
2025-10-10 16:45:04 +02:00
parent 5946f86dfa
commit 21d104280c
12 changed files with 63 additions and 18 deletions

View File

@@ -122,6 +122,13 @@
# Default: false
# BK_HIDE_ALL_MINIFIGURES=true
# Optional: Disable the individual/loose minifigures system. This hides all individual
# minifigure UI elements and prevents adding new individual minifigures. The routes remain
# accessible so existing individual minifigures can still be viewed. Users who only track
# set-based minifigures can use this to simplify the interface. Does not disable the route.
# Default: false
# BK_DISABLE_INDIVIDUAL_MINIFIGURES=false
# Optional: Hide the 'Parts' entry from the menu. Does not disable the route.
# Default: false
# BK_HIDE_ALL_PARTS=true

View File

@@ -18,6 +18,7 @@ CONFIG: Final[list[dict[str, Any]]] = [
{'n': 'DEBUG', 'c': bool},
{'n': 'DEFAULT_TABLE_PER_PAGE', 'd': 25, 'c': int},
{'n': 'DESCRIPTION_BADGE_MAX_LENGTH', 'd': 15, 'c': int},
{'n': 'DISABLE_INDIVIDUAL_MINIFIGURES', 'c': bool},
{'n': 'DOMAIN_NAME', 'e': 'DOMAIN_NAME', 'd': ''},
{'n': 'FILE_DATETIME_FORMAT', 'd': '%d/%m/%Y, %H:%M:%S'},
{'n': 'HOST', 'd': '0.0.0.0'},

View File

@@ -84,6 +84,7 @@ RESTART_REQUIRED_VARS: Final[List[str]] = [
'BK_AUTHENTICATION_KEY',
'BK_DATABASE_PATH',
'BK_DEBUG',
'BK_DISABLE_INDIVIDUAL_MINIFIGURES',
'BK_DOMAIN_NAME',
'BK_HOST',
'BK_PORT',

View File

@@ -309,6 +309,14 @@ class IndividualMinifigure(RebrickableMinifigure):
socket.progress_total = 2
try:
# Check if individual minifigures are disabled
from flask import current_app
if current_app.config.get('DISABLE_INDIVIDUAL_MINIFIGURES', False):
raise ErrorException(
'Individual minifigures system is disabled. '
'Only set-based minifigures can be added.'
)
socket.auto_progress(message='Parsing minifigure number')
figure = parse_minifig(str(data['figure']))
@@ -402,12 +410,17 @@ class IndividualMinifigure(RebrickableMinifigure):
return True
except Exception as e:
socket.fail(
message='Could not load the minifigure from Rebrickable: {error}. Data: {data}'.format(
error=str(e),
data=data,
# Check if this is the "disabled" error - if so, show cleaner message
error_msg = str(e)
if 'Individual minifigures system is disabled' in error_msg:
socket.fail(message=error_msg)
else:
socket.fail(
message='Could not load the minifigure from Rebrickable: {error}. Data: {data}'.format(
error=error_msg,
data=data,
)
)
)
if not isinstance(e, (NotFoundException, ErrorException)):
logger.debug(traceback.format_exc())

View File

@@ -26,8 +26,8 @@
<div id="add-complete"></div>
{% endif %}
<div class="mb-3">
<label for="add-set" class="form-label">{% if not bulk %}Set or Minifigure number (only one){% else %}List of sets (separated by a comma){% endif %}</label>
<input type="text" class="form-control" id="add-set" placeholder="{% if not bulk %}107-1 or fig-001234 or ...{% else %}107-1, 1642-1, ...{% endif %}"
<label for="add-set" class="form-label">{% if not bulk %}{% if not config['DISABLE_INDIVIDUAL_MINIFIGURES'] %}Set or Minifigure number (only one){% else %}Set number (only one){% endif %}{% else %}List of sets (separated by a comma){% endif %}</label>
<input type="text" class="form-control" id="add-set" placeholder="{% if not bulk %}{% if not config['DISABLE_INDIVIDUAL_MINIFIGURES'] %}107-1 or fig-001234 or ...{% else %}107-1 or ...{% endif %}{% else %}107-1, 1642-1, ...{% endif %}"
data-path="{{ path }}"
data-namespace="{{ namespace }}"
data-msg-complete="{{ messages['COMPLETE'] }}"
@@ -39,7 +39,7 @@
data-msg-import-minifigure="{{ messages['IMPORT_MINIFIGURE'] }}"
data-msg-load-minifigure="{{ messages['LOAD_MINIFIGURE'] }}"
data-msg-minifigure-loaded="{{ messages['MINIFIGURE_LOADED'] }}">
<div class="form-text">Sets: use format like 107-1. Minifigures: use format like fig-001234</div>
<div class="form-text">Sets: use format like 107-1{% if not config['DISABLE_INDIVIDUAL_MINIFIGURES'] %}. Minifigures: use format like fig-001234{% endif %}</div>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="add-no-confirm" {% if bulk %}checked disabled{% endif %}>

View File

@@ -756,6 +756,15 @@
</label>
</div>
</div>
<div class="col-md-6">
<div class="form-check form-switch">
<input class="form-check-input config-static-toggle" type="checkbox" id="static-BK_DISABLE_INDIVIDUAL_MINIFIGURES" data-var="BK_DISABLE_INDIVIDUAL_MINIFIGURES">
<label class="form-check-label" for="static-BK_DISABLE_INDIVIDUAL_MINIFIGURES">
BK_DISABLE_INDIVIDUAL_MINIFIGURES {{ config_badges('BK_DISABLE_INDIVIDUAL_MINIFIGURES') }}
<div class="text-muted small">Completely disable individual/loose minifigures system</div>
</label>
</div>
</div>
<div class="col-md-6">
<label for="static-BK_DOMAIN_NAME" class="form-label">
BK_DOMAIN_NAME {{ config_badges('BK_DOMAIN_NAME') }}

View File

@@ -23,8 +23,10 @@
<div class="accordion accordion-flush" id="minifigure-details">
{{ accordion.table(item.generic_parts(), 'Parts', item.fields.figure, 'minifigure-details', 'part/table.html', icon='shapes-line', alt=item.fields.figure, read_only=read_only)}}
{{ accordion.cards(using, 'Sets using this minifigure', 'using-inventory', 'minifigure-details', 'set/card.html', icon='hashtag') }}
{% if individual_instances is defined and individual_instances | length > 0 %}
{{ accordion.cards(individual_instances, 'Individual minifigure instances', 'individual-instances', 'minifigure-details', 'individual_minifigure/card.html', icon='package-line') }}
{% if not config['DISABLE_INDIVIDUAL_MINIFIGURES'] %}
{% if individual_instances is defined and individual_instances | length > 0 %}
{{ accordion.cards(individual_instances, 'Individual minifigure instances', 'individual-instances', 'minifigure-details', 'individual_minifigure/card.html', icon='package-line') }}
{% endif %}
{% endif %}
{{ accordion.cards(missing, 'Sets missing parts for this minifigure', 'missing-inventory', 'minifigure-details', 'set/card.html', icon='question-line') }}
{{ accordion.cards(damaged, 'Sets with damaged parts for this minifigure', 'damaged-inventory', 'minifigure-details', 'set/card.html', icon='error-warning-line') }}

View File

@@ -18,8 +18,10 @@
{% endif %}
<button id="sort-sets" type="button" class="btn btn-outline-primary mb-2"
data-sort-attribute="sets" data-sort-desc="true"><i class="ri-hashtag"></i><span class="d-none d-xl-inline"> Sets</span></button>
{% if not config['DISABLE_INDIVIDUAL_MINIFIGURES'] %}
<button id="sort-individual" type="button" class="btn btn-outline-primary mb-2"
data-sort-attribute="individual" data-sort-desc="true"><i class="ri-package-line"></i><span class="d-none d-xl-inline"> Individual</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>

View File

@@ -17,7 +17,9 @@
<td data-sort="{{ minifigure.fields.total_damaged }}">{{ minifigure.fields.total_damaged }}</td>
{% endif %}
<td data-sort="{{ minifigure.fields.total_sets }}">{{ minifigure.fields.total_sets }}</td>
{% if not config['DISABLE_INDIVIDUAL_MINIFIGURES'] %}
<td data-sort="{{ minifigure.fields.total_individual }}">{{ minifigure.fields.total_individual }}</td>
{% endif %}
</tr>
{% endfor %}
</tbody>

View File

@@ -33,7 +33,7 @@
<!-- PAGINATION MODE -->
<div class="table-responsive-sm">
<table data-table="false" class="table table-striped align-middle mb-0" id="minifigures">
{{ table.header(parts=true, quantity=true, missing=true, damaged=true, sets=true, individual=true, minifigures=false) }}
{{ table.header(parts=true, quantity=true, missing=true, damaged=true, sets=true, individual=(not config['DISABLE_INDIVIDUAL_MINIFIGURES']), minifigures=false) }}
{% include 'minifigure/table_body.html' %}
</table>
</div>
@@ -148,7 +148,7 @@
<!-- ORIGINAL MODE - Single page with client-side search -->
<div class="table-responsive-sm">
<table data-table="true" class="table table-striped align-middle {% if not all %}sortable mb-0{% endif %}" id="minifigures">
{{ table.header(parts=true, quantity=true, missing=true, damaged=true, sets=true, individual=true, minifigures=false) }}
{{ table.header(parts=true, quantity=true, missing=true, damaged=true, sets=true, individual=(not config['DISABLE_INDIVIDUAL_MINIFIGURES']), minifigures=false) }}
<tbody>
{% for minifigure in table_collection %}
<tr>

View File

@@ -5,13 +5,17 @@
<div class="card mb-3 flex-fill {% if solo %}card-solo{% endif %}">
{{ card.header(item, item.fields.name, solo=solo, icon='archive-2-line') }}
<div class="card-body border-bottom-0 {% if not solo %}p-1{% endif %}">
{{ badge.total_sets(sets | length, solo=solo, last=false) }}
{{ badge.total_individual_minifigures(individual_minifigures | length, solo=solo, last=last) }}
{{ badge.total_sets(sets | length, solo=solo, last=(config['DISABLE_INDIVIDUAL_MINIFIGURES'] or last)) }}
{% if not config['DISABLE_INDIVIDUAL_MINIFIGURES'] %}
{{ badge.total_individual_minifigures(individual_minifigures | length, solo=solo, last=last) }}
{% endif %}
</div>
{% if solo %}
<div class="accordion accordion-flush border-top" id="storage-details">
{{ accordion.cards(sets, 'Sets', 'sets-stored', 'storage-details', 'set/card.html', expanded=true, icon='hashtag') }}
{{ accordion.cards(individual_minifigures, 'Individual Minifigures', 'individual-minifigures-stored', 'storage-details', 'individual_minifigure/card.html', icon='user-line') }}
{% if not config['DISABLE_INDIVIDUAL_MINIFIGURES'] %}
{{ accordion.cards(individual_minifigures, 'Individual Minifigures', 'individual-minifigures-stored', 'storage-details', 'individual_minifigure/card.html', icon='user-line') }}
{% endif %}
</div>
<div class="card-footer"></div>
{% endif %}

View File

@@ -3,13 +3,15 @@
<div class="table-responsive-sm">
<table data-table="true" class="table table-striped align-middle" id="storage">
{{ table.header(image=false, missing=false, damaged=false, sets=true, individual=true) }}
{{ table.header(image=false, missing=false, damaged=false, sets=true, individual=(not config['DISABLE_INDIVIDUAL_MINIFIGURES'])) }}
<tbody>
{% for item in table_collection %}
<tr>
<td data-sort="{{ item.fields.name }}"><a class="text-reset" href="{{ item.url() }}">{{ item.fields.name }}</a></td>
<td>{{ item.fields.total_sets }}</td>
<td>{{ item.fields.total_individual_minifigures }}</td>
{% if not config['DISABLE_INDIVIDUAL_MINIFIGURES'] %}
<td>{{ item.fields.total_individual_minifigures }}</td>
{% endif %}
</tr>
{% endfor %}
{% if sets_no_storage is defined and minifigs_no_storage is defined %}
@@ -17,7 +19,9 @@
<tr class="table-warning">
<td data-sort="zzz-not-in-storage"><a class="text-reset" href="{{ url_for('storage.no_storage_details') }}"><em>Not in a storage location</em></a></td>
<td>{{ sets_no_storage }}</td>
<td>{{ minifigs_no_storage }}</td>
{% if not config['DISABLE_INDIVIDUAL_MINIFIGURES'] %}
<td>{{ minifigs_no_storage }}</td>
{% endif %}
</tr>
{% endif %}
{% endif %}