diff --git a/.env.sample b/.env.sample index 44d52fe..8f12939 100644 --- a/.env.sample +++ b/.env.sample @@ -168,6 +168,12 @@ # Default: 3333 # BK_PORT=3333 +# Optional: Change the default order of purchase locations. By default ordered by insertion order. +# Useful column names for this option are: +# - "bricktracker_metadata_purchase_locations"."name" ASC: storage name +# Default: "bricktracker_metadata_purchase_locations"."name" ASC +# BK_PURCHASE_LOCATION_DEFAULT_ORDER="bricktracker_metadata_purchase_locations"."name" ASC + # Optional: Shuffle the lists on the front page. # Default: false # Legacy name: RANDOM diff --git a/CHANGELOG.md b/CHANGELOG.md index b50f342..efcdb16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,8 @@ - Added: `BK_SHOW_GRID_SORT`, show the sort options on the grid by default - Added: `BK_SHOW_GRID_FILTERS`, show the filter options on the grid by default - Added: `BK_HIDE_ALL_STORAGES`, hide the "Storages" menu entry -- Added: `BK_MINIFIGURES_DEFAULT_ORDER`, ordering of storages +- Added: `BK_STORAGE_DEFAULT_ORDER`, ordering of storages +- Added: `BK_PURCHASE_LOCATION_DEFAULT_ORDER`, ordering of purchase locations ### Code @@ -39,6 +40,7 @@ - Ownership - Tags - Storage + - Purchase location - Storage - Storage content and list @@ -85,6 +87,7 @@ - Tags - Refresh - Storage + - Purchase location - Sets grid - Collapsible controls depending on screen size diff --git a/bricktracker/app.py b/bricktracker/app.py index af005d9..f0afe42 100644 --- a/bricktracker/app.py +++ b/bricktracker/app.py @@ -17,6 +17,7 @@ from bricktracker.views.admin.database import admin_database_page from bricktracker.views.admin.image import admin_image_page from bricktracker.views.admin.instructions import admin_instructions_page from bricktracker.views.admin.owner import admin_owner_page +from bricktracker.views.admin.purchase_location import admin_purchase_location_page # noqa: E501 from bricktracker.views.admin.retired import admin_retired_page from bricktracker.views.admin.status import admin_status_page from bricktracker.views.admin.storage import admin_storage_page @@ -88,6 +89,7 @@ def setup_app(app: Flask) -> None: app.register_blueprint(admin_instructions_page) app.register_blueprint(admin_retired_page) app.register_blueprint(admin_owner_page) + app.register_blueprint(admin_purchase_location_page) app.register_blueprint(admin_status_page) app.register_blueprint(admin_storage_page) app.register_blueprint(admin_tag_page) diff --git a/bricktracker/config.py b/bricktracker/config.py index 5b9788f..729049a 100644 --- a/bricktracker/config.py +++ b/bricktracker/config.py @@ -41,6 +41,7 @@ CONFIG: Final[list[dict[str, Any]]] = [ {'n': 'PARTS_DEFAULT_ORDER', 'd': '"rebrickable_parts"."name" ASC, "rebrickable_parts"."color_name" ASC, "bricktracker_parts"."spare" ASC'}, # noqa: E501 {'n': 'PARTS_FOLDER', 'd': 'parts', 's': True}, {'n': 'PORT', 'd': 3333, 'c': int}, + {'n': 'PURCHASE_LOCATION_DEFAULT_ORDER', 'd': '"bricktracker_metadata_purchase_locations"."name" ASC'}, # noqa: E501 {'n': 'RANDOM', 'e': 'RANDOM', 'c': bool}, {'n': 'REBRICKABLE_API_KEY', 'e': 'REBRICKABLE_API_KEY', 'd': ''}, {'n': 'REBRICKABLE_IMAGE_NIL', 'd': 'https://rebrickable.com/static/img/nil.png'}, # noqa: E501 diff --git a/bricktracker/metadata.py b/bricktracker/metadata.py index 0b2f61c..88d2623 100644 --- a/bricktracker/metadata.py +++ b/bricktracker/metadata.py @@ -48,7 +48,7 @@ class BrickMetadata(BrickRecord): def as_column(self, /) -> str: return '{kind}_{id}'.format( id=self.fields.id, - kind=self.kind.lower() + kind=self.kind.lower().replace(' ', '-') ) # HTML dataset name @@ -90,8 +90,6 @@ class BrickMetadata(BrickRecord): # Rename the entry def rename(self, /) -> None: - self.safe() - self.update_field('name', value=self.fields.name) # Make the name "safe" @@ -159,7 +157,7 @@ class BrickMetadata(BrickRecord): ) if rows != 1: - raise DatabaseException('Could not update the field "{field}" for {kind} {name} ({id})'.format( # noqa: E501 + raise DatabaseException('Could not update the field "{field}" for {kind} "{name}" ({id})'.format( # noqa: E501 field=field, kind=self.kind, name=self.fields.name, diff --git a/bricktracker/metadata_list.py b/bricktracker/metadata_list.py index 3b98a13..7124548 100644 --- a/bricktracker/metadata_list.py +++ b/bricktracker/metadata_list.py @@ -7,13 +7,21 @@ from .exceptions import NotFoundException from .fields import BrickRecordFields from .record_list import BrickRecordList from .set_owner import BrickSetOwner +from .set_purchase_location import BrickSetPurchaseLocation from .set_status import BrickSetStatus from .set_storage import BrickSetStorage from .set_tag import BrickSetTag logger = logging.getLogger(__name__) -T = TypeVar('T', BrickSetOwner, BrickSetStatus, BrickSetStorage, BrickSetTag) +T = TypeVar( + 'T', + BrickSetOwner, + BrickSetPurchaseLocation, + BrickSetStatus, + BrickSetStorage, + BrickSetTag +) # Lego sets metadata list diff --git a/bricktracker/record_list.py b/bricktracker/record_list.py index 23da29b..18dcb10 100644 --- a/bricktracker/record_list.py +++ b/bricktracker/record_list.py @@ -9,6 +9,7 @@ if TYPE_CHECKING: from .rebrickable_set import RebrickableSet from .set import BrickSet from .set_owner import BrickSetOwner + from .set_purchase_location import BrickSetPurchaseLocation from .set_status import BrickSetStatus from .set_storage import BrickSetStorage from .set_tag import BrickSetTag @@ -20,6 +21,7 @@ T = TypeVar( 'BrickPart', 'BrickSet', 'BrickSetOwner', + 'BrickSetPurchaseLocation', 'BrickSetStatus', 'BrickSetStorage', 'BrickSetTag', diff --git a/bricktracker/reload.py b/bricktracker/reload.py index b2247ea..38929f6 100644 --- a/bricktracker/reload.py +++ b/bricktracker/reload.py @@ -1,6 +1,7 @@ from .instructions_list import BrickInstructionsList from .retired_list import BrickRetiredList from .set_owner_list import BrickSetOwnerList +from .set_purchase_location_list import BrickSetPurchaseLocationList from .set_status_list import BrickSetStatusList from .set_storage_list import BrickSetStorageList from .set_tag_list import BrickSetTagList @@ -17,6 +18,9 @@ def reload() -> None: # Reload the set owners BrickSetOwnerList.new(force=True) + # Reload the set purchase locations + BrickSetPurchaseLocationList.new(force=True) + # Reload the set statuses BrickSetStatusList.new(force=True) diff --git a/bricktracker/set.py b/bricktracker/set.py index 6368d40..fb97209 100644 --- a/bricktracker/set.py +++ b/bricktracker/set.py @@ -10,6 +10,7 @@ from .minifigure_list import BrickMinifigureList from .part_list import BrickPartList from .rebrickable_set import RebrickableSet from .set_owner_list import BrickSetOwnerList +from .set_purchase_location_list import BrickSetPurchaseLocationList from .set_status_list import BrickSetStatusList from .set_storage_list import BrickSetStorageList from .set_tag_list import BrickSetTagList @@ -63,6 +64,13 @@ class BrickSet(RebrickableSet): ) self.fields.storage = storage.fields.id + # Save the purchase location + purchase_location = BrickSetPurchaseLocationList.get( + data.get('purchase_location', ''), + allow_none=True + ) + self.fields.purchase_location = purchase_location.fields.id + # Insert into database self.insert(commit=False) diff --git a/bricktracker/set_list.py b/bricktracker/set_list.py index deaf269..a8f5faa 100644 --- a/bricktracker/set_list.py +++ b/bricktracker/set_list.py @@ -5,6 +5,8 @@ from flask import current_app from .record_list import BrickRecordList from .set_owner import BrickSetOwner from .set_owner_list import BrickSetOwnerList +from .set_purchase_location import BrickSetPurchaseLocation +from .set_purchase_location_list import BrickSetPurchaseLocationList from .set_status_list import BrickSetStatusList from .set_storage import BrickSetStorage from .set_storage_list import BrickSetStorageList @@ -175,6 +177,8 @@ def set_metadata_lists( str, Union[ list[BrickSetOwner], + list[BrickSetPurchaseLocation], + BrickSetPurchaseLocation, list[BrickSetStorage], BrickSetStorageList, list[BrickSetTag] @@ -182,6 +186,7 @@ def set_metadata_lists( ]: return { 'brickset_owners': BrickSetOwnerList.list(), + 'brickset_purchase_locations': BrickSetPurchaseLocationList.list(as_class=as_class), # noqa: E501 'brickset_storages': BrickSetStorageList.list(as_class=as_class), 'brickset_tags': BrickSetTagList.list(), } diff --git a/bricktracker/set_purchase_location.py b/bricktracker/set_purchase_location.py new file mode 100644 index 0000000..801ccf8 --- /dev/null +++ b/bricktracker/set_purchase_location.py @@ -0,0 +1,13 @@ +from .metadata import BrickMetadata + + +# Lego set purchase location metadata +class BrickSetPurchaseLocation(BrickMetadata): + kind: str = 'purchase location' + + # Queries + delete_query: str = 'set/metadata/purchase_location/delete' + insert_query: str = 'set/metadata/purchase_location/insert' + select_query: str = 'set/metadata/purchase_location/select' + update_field_query: str = 'set/metadata/purchase_location/update/field' + update_set_value_query: str = 'set/metadata/purchase_location/update/value' diff --git a/bricktracker/set_purchase_location_list.py b/bricktracker/set_purchase_location_list.py new file mode 100644 index 0000000..3ffae4b --- /dev/null +++ b/bricktracker/set_purchase_location_list.py @@ -0,0 +1,42 @@ +import logging +from typing import Self + +from flask import current_app + +from .metadata_list import BrickMetadataList +from .set_purchase_location import BrickSetPurchaseLocation + +logger = logging.getLogger(__name__) + + +# Lego sets purchase location list +class BrickSetPurchaseLocationList( + BrickMetadataList[BrickSetPurchaseLocation] +): + kind: str = 'set purchase locations' + + # Queries + select_query = 'set/metadata/purchase_location/list' + all_query = 'set/metadata/purchase_location/all' + + # Set value endpoint + set_value_endpoint: str = 'set.update_purchase_location' + + # Load all purchase locations + @classmethod + def all(cls, /) -> Self: + new = cls.new() + new.override() + + for record in new.select( + override_query=cls.all_query, + order=current_app.config['PURCHASE_LOCATION_DEFAULT_ORDER'] + ): + new.records.append(new.model(record=record)) + + return new + + # Instantiate the list with the proper class + @classmethod + def new(cls, /, *, force: bool = False) -> Self: + return cls(BrickSetPurchaseLocation, force=force) diff --git a/bricktracker/sql/set/base/base.sql b/bricktracker/sql/set/base/base.sql index fbc86e0..333868d 100644 --- a/bricktracker/sql/set/base/base.sql +++ b/bricktracker/sql/set/base/base.sql @@ -1,6 +1,7 @@ SELECT {% block id %}{% endblock %} "bricktracker_sets"."storage", + "bricktracker_sets"."purchase_location", "rebrickable_sets"."set", "rebrickable_sets"."number", "rebrickable_sets"."version", diff --git a/bricktracker/sql/set/insert.sql b/bricktracker/sql/set/insert.sql index 9a46f88..bc933c3 100644 --- a/bricktracker/sql/set/insert.sql +++ b/bricktracker/sql/set/insert.sql @@ -1,9 +1,11 @@ INSERT OR IGNORE INTO "bricktracker_sets" ( "id", "set", - "storage" + "storage", + "purchase_location" ) VALUES ( :id, :set, - :storage + :storage, + :purchase_location ) diff --git a/bricktracker/sql/set/metadata/purchase_location/base.sql b/bricktracker/sql/set/metadata/purchase_location/base.sql new file mode 100644 index 0000000..8ac33ca --- /dev/null +++ b/bricktracker/sql/set/metadata/purchase_location/base.sql @@ -0,0 +1,6 @@ +SELECT + "bricktracker_metadata_purchase_locations"."id", + "bricktracker_metadata_purchase_locations"."name" +FROM "bricktracker_metadata_purchase_locations" + +{% block where %}{% endblock %} diff --git a/bricktracker/sql/set/metadata/purchase_location/delete.sql b/bricktracker/sql/set/metadata/purchase_location/delete.sql new file mode 100644 index 0000000..489dfd0 --- /dev/null +++ b/bricktracker/sql/set/metadata/purchase_location/delete.sql @@ -0,0 +1,10 @@ +BEGIN TRANSACTION; + +DELETE FROM "bricktracker_metadata_purchase_locations" +WHERE "bricktracker_metadata_purchase_locations"."id" IS NOT DISTINCT FROM '{{ id }}'; + +UPDATE "bricktracker_sets" +SET "purchase_location" = NULL +WHERE "bricktracker_sets"."purchase_location" IS NOT DISTINCT FROM '{{ id }}'; + +COMMIT; \ No newline at end of file diff --git a/bricktracker/sql/set/metadata/purchase_location/insert.sql b/bricktracker/sql/set/metadata/purchase_location/insert.sql new file mode 100644 index 0000000..22fc587 --- /dev/null +++ b/bricktracker/sql/set/metadata/purchase_location/insert.sql @@ -0,0 +1,11 @@ +BEGIN TRANSACTION; + +INSERT INTO "bricktracker_metadata_purchase_locations" ( + "id", + "name" +) VALUES ( + '{{ id }}', + '{{ name }}' +); + +COMMIT; \ No newline at end of file diff --git a/bricktracker/sql/set/metadata/purchase_location/list.sql b/bricktracker/sql/set/metadata/purchase_location/list.sql new file mode 100644 index 0000000..2a0813b --- /dev/null +++ b/bricktracker/sql/set/metadata/purchase_location/list.sql @@ -0,0 +1 @@ +{% extends 'set/metadata/purchase_location/base.sql' %} diff --git a/bricktracker/sql/set/metadata/purchase_location/select.sql b/bricktracker/sql/set/metadata/purchase_location/select.sql new file mode 100644 index 0000000..a9e6161 --- /dev/null +++ b/bricktracker/sql/set/metadata/purchase_location/select.sql @@ -0,0 +1,5 @@ +{% extends 'set/metadata/purchase_location/base.sql' %} + +{% block where %} +WHERE "bricktracker_metadata_purchase_locations"."id" IS NOT DISTINCT FROM :id +{% endblock %} \ No newline at end of file diff --git a/bricktracker/sql/set/metadata/purchase_location/update/field.sql b/bricktracker/sql/set/metadata/purchase_location/update/field.sql new file mode 100644 index 0000000..323d98d --- /dev/null +++ b/bricktracker/sql/set/metadata/purchase_location/update/field.sql @@ -0,0 +1,3 @@ +UPDATE "bricktracker_metadata_purchase_locations" +SET "{{field}}" = :value +WHERE "bricktracker_metadata_purchase_locations"."id" IS NOT DISTINCT FROM :id diff --git a/bricktracker/sql/set/metadata/purchase_location/update/value.sql b/bricktracker/sql/set/metadata/purchase_location/update/value.sql new file mode 100644 index 0000000..d27469e --- /dev/null +++ b/bricktracker/sql/set/metadata/purchase_location/update/value.sql @@ -0,0 +1,3 @@ +UPDATE "bricktracker_sets" +SET "purchase_location" = :value +WHERE "bricktracker_sets"."id" IS NOT DISTINCT FROM :set_id diff --git a/bricktracker/views/admin/admin.py b/bricktracker/views/admin/admin.py index 08cdafc..749b3df 100644 --- a/bricktracker/views/admin/admin.py +++ b/bricktracker/views/admin/admin.py @@ -10,6 +10,8 @@ from ...rebrickable_image import RebrickableImage from ...retired_list import BrickRetiredList from ...set_owner import BrickSetOwner from ...set_owner_list import BrickSetOwnerList +from ...set_purchase_location import BrickSetPurchaseLocation +from ...set_purchase_location_list import BrickSetPurchaseLocationList from ...set_storage import BrickSetStorage from ...set_storage_list import BrickSetStorageList from ...set_status import BrickSetStatus @@ -36,6 +38,7 @@ def admin() -> str: database_version: int = -1 instructions: BrickInstructionsList | None = None metadata_owners: list[BrickSetOwner] = [] + metadata_purchase_locations: list[BrickSetPurchaseLocation] = [] metadata_statuses: list[BrickSetStatus] = [] metadata_storages: list[BrickSetStorage] = [] metadata_tags: list[BrickSetTag] = [] @@ -54,6 +57,7 @@ def admin() -> str: instructions = BrickInstructionsList() metadata_owners = BrickSetOwnerList.list() + metadata_purchase_locations = BrickSetPurchaseLocationList.list() metadata_statuses = BrickSetStatusList.list(all=True) metadata_storages = BrickSetStorageList.list() metadata_tags = BrickSetTagList.list() @@ -81,6 +85,7 @@ def admin() -> str: open_instructions = request.args.get('open_instructions', None) open_logout = request.args.get('open_logout', None) open_owner = request.args.get('open_owner', None) + open_purchase_location = request.args.get('open_purchase_location', None) open_retired = request.args.get('open_retired', None) open_status = request.args.get('open_status', None) open_storage = request.args.get('open_storage', None) @@ -89,6 +94,7 @@ def admin() -> str: open_metadata = ( open_owner or + open_purchase_location or open_status or open_storage or open_tag @@ -113,6 +119,7 @@ def admin() -> str: database_version=database_version, instructions=instructions, metadata_owners=metadata_owners, + metadata_purchase_locations=metadata_purchase_locations, metadata_statuses=metadata_statuses, metadata_storages=metadata_storages, metadata_tags=metadata_tags, @@ -126,12 +133,14 @@ def admin() -> str: open_logout=open_logout, open_metadata=open_metadata, open_owner=open_owner, + open_purchase_location=open_purchase_location, open_retired=open_retired, open_status=open_status, open_storage=open_storage, open_tag=open_tag, open_theme=open_theme, owner_error=request.args.get('owner_error'), + purchase_location_error=request.args.get('purchase_location_error'), retired=BrickRetiredList(), status_error=request.args.get('status_error'), storage_error=request.args.get('storage_error'), diff --git a/bricktracker/views/admin/purchase_location.py b/bricktracker/views/admin/purchase_location.py new file mode 100644 index 0000000..48e7b7d --- /dev/null +++ b/bricktracker/views/admin/purchase_location.py @@ -0,0 +1,84 @@ +from flask import ( + Blueprint, + redirect, + request, + render_template, + url_for, +) +from flask_login import login_required +from werkzeug.wrappers.response import Response + +from ..exceptions import exception_handler +from ...reload import reload +from ...set_purchase_location import BrickSetPurchaseLocation + +admin_purchase_location_page = Blueprint( + 'admin_purchase_location', + __name__, + url_prefix='/admin/purchase_location' +) + + +# Add a metadata purchase location +@admin_purchase_location_page.route('/add', methods=['POST']) +@login_required +@exception_handler( + __file__, + post_redirect='admin.admin', + error_name='purchase_location_error', + open_purchase_location=True +) +def add() -> Response: + BrickSetPurchaseLocation().from_form(request.form).insert() + + reload() + + return redirect(url_for('admin.admin', open_purchase_location=True)) + + +# Delete the metadata purchase location +@admin_purchase_location_page.route('/delete', methods=['GET']) +@login_required +@exception_handler(__file__) +def delete(*, id: str) -> str: + return render_template( + 'admin.html', + delete_purchase_location=True, + purchase_location=BrickSetPurchaseLocation().select_specific(id), + error=request.args.get('purchase_location_error') + ) + + +# Actually delete the metadata purchase location +@admin_purchase_location_page.route('/delete', methods=['POST']) +@login_required +@exception_handler( + __file__, + post_redirect='admin_purchase_location.delete', + error_name='purchase_location_error' +) +def do_delete(*, id: str) -> Response: + purchase_location = BrickSetPurchaseLocation().select_specific(id) + purchase_location.delete() + + reload() + + return redirect(url_for('admin.admin', open_purchase_location=True)) + + +# Rename the metadata purchase location +@admin_purchase_location_page.route('/rename', methods=['POST']) +@login_required +@exception_handler( + __file__, + post_redirect='admin.admin', + error_name='purchase_location_error', + open_purchase_location=True +) +def rename(*, id: str) -> Response: + purchase_location = BrickSetPurchaseLocation().select_specific(id) + purchase_location.from_form(request.form).rename() + + reload() + + return redirect(url_for('admin.admin', open_purchase_location=True)) diff --git a/bricktracker/views/set.py b/bricktracker/views/set.py index 7f397da..0777f4e 100644 --- a/bricktracker/views/set.py +++ b/bricktracker/views/set.py @@ -18,6 +18,7 @@ from ..part import BrickPart from ..set import BrickSet from ..set_list import BrickSetList, set_metadata_lists from ..set_owner_list import BrickSetOwnerList +from ..set_purchase_location_list import BrickSetPurchaseLocationList from ..set_status_list import BrickSetStatusList from ..set_storage_list import BrickSetStorageList from ..set_tag_list import BrickSetTagList @@ -40,6 +41,25 @@ def list() -> str: ) +# Change the value of purchase location +@set_page.route('//purchase_location', methods=['POST']) +@login_required +@exception_handler(__file__, json=True) +def update_purchase_location(*, id: str) -> Response: + brickset = BrickSet().select_light(id) + purchase_location = BrickSetPurchaseLocationList.get( + request.json.get('value', ''), # type: ignore + allow_none=True + ) + + value = purchase_location.update_set_value( + brickset, + value=purchase_location.fields.id + ) + + return jsonify({'value': value}) + + # Change the state of a owner @set_page.route('//owner/', methods=['POST']) @login_required diff --git a/static/scripts/socket/set.js b/static/scripts/socket/set.js index 311eac5..8a4e5bb 100644 --- a/static/scripts/socket/set.js +++ b/static/scripts/socket/set.js @@ -16,6 +16,7 @@ class BrickSetSocket extends BrickSocket { this.html_input = document.getElementById(`${id}-set`); this.html_no_confim = document.getElementById(`${id}-no-confirm`); this.html_owners = document.getElementById(`${id}-owners`); + this.html_purchase_location = document.getElementById(`${id}-purchase-location`); this.html_storage = document.getElementById(`${id}-storage`); this.html_tags = document.getElementById(`${id}-tags`); @@ -152,6 +153,12 @@ class BrickSetSocket extends BrickSocket { }); } + // Grab the purchase location + let purchase_location = null; + if (this.html_purchase_location) { + purchase_location = this.html_purchase_location.value; + } + // Grab the storage let storage = null; if (this.html_storage) { @@ -177,6 +184,7 @@ class BrickSetSocket extends BrickSocket { this.socket.emit(this.messages.IMPORT_SET, { set: (set !== undefined) ? set : this.html_input.value, owners: owners, + purchase_location: purchase_location, storage: storage, tags: tags, refresh: this.refresh @@ -293,6 +301,10 @@ class BrickSetSocket extends BrickSocket { this.html_owners.querySelectorAll('input').forEach(input => input.disabled = !enabled); } + if (this.html_purchase_location) { + this.html_purchase_location.disabled = !enabled; + } + if (this.html_storage) { this.html_storage.disabled = !enabled; } diff --git a/templates/add.html b/templates/add.html index 9a0deeb..d9a9462 100644 --- a/templates/add.html +++ b/templates/add.html @@ -51,9 +51,22 @@ {{ accordion.footer() }} {% endif %} + {% if brickset_purchase_locations | length %} + {{ accordion.header('Purchase location', 'purchase-location', 'metadata', icon='building-line') }} + +
+ +
+ {{ accordion.footer() }} + {% endif %} {% if brickset_storages | length %} {{ accordion.header('Storage', 'storage', 'metadata', icon='archive-2-line') }} - +
+ +
+ +
+ Delete +
+ + + {% endfor %} + {% else %} +
  • No purchase location found.
  • + {% endif %} +
  • +
    +
    + +
    +
    Name
    + +
    +
    +
    + +
    +
    +
  • + +{{ accordion.footer() }} diff --git a/templates/admin/purchase_location/delete.html b/templates/admin/purchase_location/delete.html new file mode 100644 index 0000000..c53d8c2 --- /dev/null +++ b/templates/admin/purchase_location/delete.html @@ -0,0 +1,19 @@ +{% import 'macro/accordion.html' as accordion %} + +{{ accordion.header('Set purchase locations danger zone', 'purchase-location-danger', 'admin', expanded=true, danger=true, class='text-end') }} +
    + {% if purchase_location_error %}{% endif %} + +
    +
    +
    +
    Name
    + +
    +
    +
    +
    + Back to the admin + +
    +{{ accordion.footer() }} diff --git a/templates/macro/badge.html b/templates/macro/badge.html index ea2be58..f1c9f89 100644 --- a/templates/macro/badge.html +++ b/templates/macro/badge.html @@ -65,6 +65,18 @@ {% endif %} {% endmacro %} +{% macro purchase_location(item, purchase_locations, solo=false, last=false) %} + {% if purchase_locations and item.fields.purchase_location in purchase_locations.mapping %} + {% set purchase_location = purchase_locations.mapping[item.fields.purchase_location] %} + {% if last %} + {% set tooltip=purchase_location.fields.name %} + {% else %} + {% set text=purchase_location.fields.name %} + {% endif %} + {{ badge(check=purchase_location, solo=solo, last=last, color='light border', icon='building-line', text=text, tooltip=tooltip) }} + {% endif %} +{% endmacro %} + {% macro set(set, solo=false, last=false, url=None, id=None) %} {% if id %} {% set url=url_for('set.details', id=id) %} diff --git a/templates/set/card.html b/templates/set/card.html index 2064206..7c2525d 100644 --- a/templates/set/card.html +++ b/templates/set/card.html @@ -17,6 +17,11 @@ {% if not config['HIDE_TABLE_DAMAGED_PARTS'] %} data-has-damaged="{{ (item.fields.total_damaged > 0) | int }}" data-damaged="{{ item.fields.total_damaged }}" {% endif %} + data-has-purchase-location="{{ item.fields.purchase_location is not none | int }}" + {% if item.fields.purchase_location is not none %} + data-purchase-location="{{ item.fields.purchase_location }}" + {% if item.fields.purchase_location in brickset_purchase_locations.mapping %}data-search-purchase-location="{{ brickset_purchase_locations.mapping[item.fields.purchase_location].fields.name | lower }}"{% endif %} + {% endif %} data-has-storage="{{ item.fields.storage is not none | int }}" {% if item.fields.storage is not none %} data-storage="{{ item.fields.storage }}" @@ -63,6 +68,7 @@ {{ badge.owner(item, owner, solo=solo, last=last) }} {% endfor %} {{ badge.storage(item, brickset_storages, solo=solo, last=last) }} + {{ badge.purchase_location(item, brickset_purchase_locations, solo=solo, last=last) }} {% if not last %} {% if not solo %} {{ badge.instructions(item, solo=solo, last=last) }} diff --git a/templates/set/filter.html b/templates/set/filter.html index 8f3b410..24454c2 100644 --- a/templates/set/filter.html +++ b/templates/set/filter.html @@ -60,6 +60,22 @@ {% endif %} + {% if brickset_purchase_locations | length %} +
    + +
    + Purchase location + +
    +
    + {% endif %} {% if brickset_storages | length %}
    diff --git a/templates/set/management.html b/templates/set/management.html index 1a8c2b7..8d675f9 100644 --- a/templates/set/management.html +++ b/templates/set/management.html @@ -1,44 +1,53 @@ {% if g.login.is_authenticated() %} {{ accordion.header('Management', 'set-management', 'set-details', icon='settings-4-line', class='p-0') }} {{ accordion.header('Owners', 'owner', 'set-management', icon='group-line', class='p-0') }} -
      - {% if brickset_owners | length %} - {% for owner in brickset_owners %} -
    • {{ form.checkbox(item, owner, delete=delete) }}
    • - {% endfor %} - {% else %} -
    • No owner found.
    • +
        + {% if brickset_owners | length %} + {% for owner in brickset_owners %} +
      • {{ form.checkbox(item, owner, delete=delete) }}
      • + {% endfor %} + {% else %} +
      • No owner found.
      • + {% endif %} +
      + + {{ accordion.footer() }} + {{ accordion.header('Purchase location', 'purchase-location', 'set-management', icon='building-line') }} + {% if brickset_purchase_locations | length %} + {{ form.select('Purchase location', item, 'purchase_location', brickset_purchase_locations, delete=delete) }} + {% else %} +

      No purchase location found.

      {% endif %} -
    - - {{ accordion.footer() }} +
    + Manage the set purchase locations + {{ accordion.footer() }} {{ accordion.header('Storage', 'storage', 'set-management', icon='archive-2-line') }} - {% if brickset_storages | length %} - {{ form.select('Storage', item, 'storage', brickset_storages, delete=delete) }} - {% else %} -

    No storage found.

    - {% endif %} -
    - Manage the set storages - {{ accordion.footer() }} - {{ accordion.header('Tags', 'tag', 'set-management', icon='price-tag-2-line', class='p-0') }} -
      - {% if brickset_tags | length %} - {% for tag in brickset_tags %} -
    • {{ form.checkbox(item, tag, delete=delete) }}
    • - {% endfor %} - {% else %} -
    • No tag found.
    • + {% if brickset_storages | length %} + {{ form.select('Storage', item, 'storage', brickset_storages, delete=delete) }} + {% else %} +

      No storage found.

      {% endif %} -
    - - {{ accordion.footer() }} +
    + Manage the set storages + {{ accordion.footer() }} + {{ accordion.header('Tags', 'tag', 'set-management', icon='price-tag-2-line', class='p-0') }} +
      + {% if brickset_tags | length %} + {% for tag in brickset_tags %} +
    • {{ form.checkbox(item, tag, delete=delete) }}
    • + {% endfor %} + {% else %} +
    • No tag found.
    • + {% endif %} +
    + + {{ accordion.footer() }} {{ accordion.header('Data', 'data', 'set-management', icon='database-2-line') }} - Refresh the set data + Refresh the set data {{ accordion.footer() }} {{ accordion.footer() }} {% endif %} diff --git a/templates/sets.html b/templates/sets.html index 5af93c9..4832160 100644 --- a/templates/sets.html +++ b/templates/sets.html @@ -10,7 +10,7 @@
    Search - +