Set storage details
This commit is contained in:
parent
f9e9edd506
commit
4e3ae49187
16
.env.sample
16
.env.sample
@ -91,6 +91,11 @@
|
|||||||
# Default: false
|
# Default: false
|
||||||
# BK_HIDE_ADMIN=true
|
# BK_HIDE_ADMIN=true
|
||||||
|
|
||||||
|
# Optional: Hide the 'Problems' entry from the menu. Does not disable the route.
|
||||||
|
# Default: false
|
||||||
|
# Legacy name: BK_HIDE_MISSING_PARTS
|
||||||
|
# BK_HIDE_ALL_PROBLEMS_PARTS=true
|
||||||
|
|
||||||
# Optional: Hide the 'Instructions' entry from the menu. Does not disable the route.
|
# Optional: Hide the 'Instructions' entry from the menu. Does not disable the route.
|
||||||
# Default: false
|
# Default: false
|
||||||
# BK_HIDE_ALL_INSTRUCTIONS=true
|
# BK_HIDE_ALL_INSTRUCTIONS=true
|
||||||
@ -107,10 +112,9 @@
|
|||||||
# Default: false
|
# Default: false
|
||||||
# BK_HIDE_ALL_SETS=true
|
# BK_HIDE_ALL_SETS=true
|
||||||
|
|
||||||
# Optional: Hide the 'Problems' entry from the menu. Does not disable the route.
|
# Optional: Hide the 'Storages' entry from the menu. Does not disable the route.
|
||||||
# Default: false
|
# Default: false
|
||||||
# Legacy name: BK_HIDE_MISSING_PARTS
|
# BK_HIDE_ALL_STORAGES=true
|
||||||
# BK_HIDE_ALL_PROBLEMS_PARTS=true
|
|
||||||
|
|
||||||
# Optional: Hide the 'Instructions' entry in a Set card
|
# Optional: Hide the 'Instructions' entry in a Set card
|
||||||
# Default: false
|
# Default: false
|
||||||
@ -255,6 +259,12 @@
|
|||||||
# Default: /bricksocket/
|
# Default: /bricksocket/
|
||||||
# BK_SOCKET_PATH=custompath
|
# BK_SOCKET_PATH=custompath
|
||||||
|
|
||||||
|
# Optional: Change the default order of storages. By default ordered by insertion order.
|
||||||
|
# Useful column names for this option are:
|
||||||
|
# - "bricktracker_metadata_storages"."name" ASC: storage name
|
||||||
|
# Default: "bricktracker_metadata_storages"."name" ASC
|
||||||
|
# BK_MINIFIGURES_DEFAULT_ORDER="bricktracker_metadata_storages"."name" ASC
|
||||||
|
|
||||||
# Optional: URL to the themes.csv.gz on Rebrickable
|
# Optional: URL to the themes.csv.gz on Rebrickable
|
||||||
# Default: https://cdn.rebrickable.com/media/downloads/themes.csv.gz
|
# Default: https://cdn.rebrickable.com/media/downloads/themes.csv.gz
|
||||||
# BK_THEMES_FILE_URL=
|
# BK_THEMES_FILE_URL=
|
||||||
|
11
CHANGELOG.md
11
CHANGELOG.md
@ -13,6 +13,8 @@
|
|||||||
- Added: `BK_HIDE_TABLE_DAMAGED_PARTS`, hide the Damaged column in all tables
|
- Added: `BK_HIDE_TABLE_DAMAGED_PARTS`, hide the Damaged column in all tables
|
||||||
- Added: `BK_SHOW_GRID_SORT`, show the sort options on the grid by default
|
- 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_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
|
||||||
|
|
||||||
### Code
|
### Code
|
||||||
|
|
||||||
@ -28,7 +30,7 @@
|
|||||||
- Deduplicate
|
- Deduplicate
|
||||||
- Compute number of parts
|
- Compute number of parts
|
||||||
|
|
||||||
Parts
|
- Parts
|
||||||
- Damaged parts
|
- Damaged parts
|
||||||
|
|
||||||
- Sets
|
- Sets
|
||||||
@ -38,6 +40,9 @@ Parts
|
|||||||
- Tags
|
- Tags
|
||||||
- Storage
|
- Storage
|
||||||
|
|
||||||
|
- Storage
|
||||||
|
- Storage content and list
|
||||||
|
|
||||||
- Socket
|
- Socket
|
||||||
- Add decorator for rebrickable, authenticated and threaded socket actions
|
- Add decorator for rebrickable, authenticated and threaded socket actions
|
||||||
|
|
||||||
@ -86,6 +91,10 @@ Parts
|
|||||||
- Manually collapsible filters (with configuration variable for default state)
|
- Manually collapsible filters (with configuration variable for default state)
|
||||||
- Manually collapsible sort (with configuration variable for default state)
|
- Manually collapsible sort (with configuration variable for default state)
|
||||||
|
|
||||||
|
- Storage
|
||||||
|
- Storage list
|
||||||
|
- Storage content
|
||||||
|
|
||||||
## 1.1.1: PDF Instructions Download
|
## 1.1.1: PDF Instructions Download
|
||||||
|
|
||||||
### Instructions
|
### Instructions
|
||||||
|
@ -29,6 +29,7 @@ from bricktracker.views.login import login_page
|
|||||||
from bricktracker.views.minifigure import minifigure_page
|
from bricktracker.views.minifigure import minifigure_page
|
||||||
from bricktracker.views.part import part_page
|
from bricktracker.views.part import part_page
|
||||||
from bricktracker.views.set import set_page
|
from bricktracker.views.set import set_page
|
||||||
|
from bricktracker.views.storage import storage_page
|
||||||
from bricktracker.views.wish import wish_page
|
from bricktracker.views.wish import wish_page
|
||||||
|
|
||||||
|
|
||||||
@ -77,6 +78,7 @@ def setup_app(app: Flask) -> None:
|
|||||||
app.register_blueprint(minifigure_page)
|
app.register_blueprint(minifigure_page)
|
||||||
app.register_blueprint(part_page)
|
app.register_blueprint(part_page)
|
||||||
app.register_blueprint(set_page)
|
app.register_blueprint(set_page)
|
||||||
|
app.register_blueprint(storage_page)
|
||||||
app.register_blueprint(wish_page)
|
app.register_blueprint(wish_page)
|
||||||
|
|
||||||
# Register admin routes
|
# Register admin routes
|
||||||
|
@ -29,6 +29,7 @@ CONFIG: Final[list[dict[str, Any]]] = [
|
|||||||
{'n': 'HIDE_ALL_MINIFIGURES', 'c': bool},
|
{'n': 'HIDE_ALL_MINIFIGURES', 'c': bool},
|
||||||
{'n': 'HIDE_ALL_PARTS', 'c': bool},
|
{'n': 'HIDE_ALL_PARTS', 'c': bool},
|
||||||
{'n': 'HIDE_ALL_SETS', 'c': bool},
|
{'n': 'HIDE_ALL_SETS', 'c': bool},
|
||||||
|
{'n': 'HIDE_ALL_STORAGES', 'c': bool},
|
||||||
{'n': 'HIDE_ALL_PROBLEMS_PARTS', 'e': 'BK_HIDE_MISSING_PARTS', 'c': bool},
|
{'n': 'HIDE_ALL_PROBLEMS_PARTS', 'e': 'BK_HIDE_MISSING_PARTS', 'c': bool},
|
||||||
{'n': 'HIDE_SET_INSTRUCTIONS', 'c': bool},
|
{'n': 'HIDE_SET_INSTRUCTIONS', 'c': bool},
|
||||||
{'n': 'HIDE_TABLE_DAMAGED_PARTS', 'c': bool},
|
{'n': 'HIDE_TABLE_DAMAGED_PARTS', 'c': bool},
|
||||||
@ -59,6 +60,7 @@ CONFIG: Final[list[dict[str, Any]]] = [
|
|||||||
{'n': 'SKIP_SPARE_PARTS', 'c': bool},
|
{'n': 'SKIP_SPARE_PARTS', 'c': bool},
|
||||||
{'n': 'SOCKET_NAMESPACE', 'd': 'bricksocket'},
|
{'n': 'SOCKET_NAMESPACE', 'd': 'bricksocket'},
|
||||||
{'n': 'SOCKET_PATH', 'd': '/bricksocket/'},
|
{'n': 'SOCKET_PATH', 'd': '/bricksocket/'},
|
||||||
|
{'n': 'STORAGE_DEFAULT_ORDER', 'd': '"bricktracker_metadata_storages"."name" ASC'}, # noqa: E501
|
||||||
{'n': 'THEMES_FILE_URL', 'd': 'https://cdn.rebrickable.com/media/downloads/themes.csv.gz'}, # noqa: E501
|
{'n': 'THEMES_FILE_URL', 'd': 'https://cdn.rebrickable.com/media/downloads/themes.csv.gz'}, # noqa: E501
|
||||||
{'n': 'THEMES_PATH', 'd': './themes.csv'},
|
{'n': 'THEMES_PATH', 'd': './themes.csv'},
|
||||||
{'n': 'TIMEZONE', 'd': 'Etc/UTC'},
|
{'n': 'TIMEZONE', 'd': 'Etc/UTC'},
|
||||||
|
@ -43,8 +43,7 @@ class BrickMetadataList(BrickRecordList[T]):
|
|||||||
|
|
||||||
# Records override (masking the class variables with instance ones)
|
# Records override (masking the class variables with instance ones)
|
||||||
if records is not None:
|
if records is not None:
|
||||||
self.records = []
|
self.override()
|
||||||
self.mapping = {}
|
|
||||||
|
|
||||||
for metadata in records:
|
for metadata in records:
|
||||||
self.records.append(metadata)
|
self.records.append(metadata)
|
||||||
@ -79,6 +78,13 @@ class BrickMetadataList(BrickRecordList[T]):
|
|||||||
def filter(self) -> list[T]:
|
def filter(self) -> list[T]:
|
||||||
return self.records
|
return self.records
|
||||||
|
|
||||||
|
# Add a layer of override data
|
||||||
|
def override(self) -> None:
|
||||||
|
self.fields = BrickRecordFields()
|
||||||
|
|
||||||
|
self.records = []
|
||||||
|
self.mapping = {}
|
||||||
|
|
||||||
# Return the items as columns for a select
|
# Return the items as columns for a select
|
||||||
@classmethod
|
@classmethod
|
||||||
def as_columns(cls, /, **kwargs) -> str:
|
def as_columns(cls, /, **kwargs) -> str:
|
||||||
|
@ -14,6 +14,7 @@ NAVBAR: Final[list[dict[str, Any]]] = [
|
|||||||
{'e': 'part.problem', 't': 'Problems', 'i': 'error-warning-line', 'f': 'HIDE_ALL_PROBLEMS_PARTS'}, # noqa: E501
|
{'e': 'part.problem', 't': 'Problems', 'i': 'error-warning-line', 'f': 'HIDE_ALL_PROBLEMS_PARTS'}, # noqa: E501
|
||||||
{'e': 'minifigure.list', 't': 'Minifigures', 'i': 'group-line', 'f': 'HIDE_ALL_MINIFIGURES'}, # noqa: E501
|
{'e': 'minifigure.list', 't': 'Minifigures', 'i': 'group-line', 'f': 'HIDE_ALL_MINIFIGURES'}, # noqa: E501
|
||||||
{'e': 'instructions.list', 't': 'Instructions', 'i': 'file-line', 'f': 'HIDE_ALL_INSTRUCTIONS'}, # noqa: E501
|
{'e': 'instructions.list', 't': 'Instructions', 'i': 'file-line', 'f': 'HIDE_ALL_INSTRUCTIONS'}, # noqa: E501
|
||||||
|
{'e': 'storage.list', 't': 'Storages', 'i': 'archive-2-line', 'f': 'HIDE_ALL_STORAGES'}, # noqa: E501
|
||||||
{'e': 'wish.list', 't': 'Wishlist', 'i': 'gift-line', 'f': 'HIDE_WISHES'},
|
{'e': 'wish.list', 't': 'Wishlist', 'i': 'gift-line', 'f': 'HIDE_WISHES'},
|
||||||
{'e': 'admin.admin', 't': 'Admin', 'i': 'settings-4-line', 'f': 'HIDE_ADMIN'}, # noqa: E501
|
{'e': 'admin.admin', 't': 'Admin', 'i': 'settings-4-line', 'f': 'HIDE_ADMIN'}, # noqa: E501
|
||||||
]
|
]
|
||||||
|
@ -214,7 +214,11 @@ class BrickSet(RebrickableSet):
|
|||||||
|
|
||||||
# Compute the url for the refresh button
|
# Compute the url for the refresh button
|
||||||
def url_for_refresh(self, /) -> str:
|
def url_for_refresh(self, /) -> str:
|
||||||
return url_for(
|
return url_for('set.refresh', id=self.fields.id)
|
||||||
'set.refresh',
|
|
||||||
id=self.fields.id,
|
# Compute the url for the set storage
|
||||||
)
|
def url_for_storage(self, /) -> str:
|
||||||
|
if self.fields.storage is not None:
|
||||||
|
return url_for('storage.details', id=self.fields.storage)
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
@ -5,6 +5,7 @@ from flask import current_app
|
|||||||
from .record_list import BrickRecordList
|
from .record_list import BrickRecordList
|
||||||
from .set_owner_list import BrickSetOwnerList
|
from .set_owner_list import BrickSetOwnerList
|
||||||
from .set_status_list import BrickSetStatusList
|
from .set_status_list import BrickSetStatusList
|
||||||
|
from .set_storage import BrickSetStorage
|
||||||
from .set_tag_list import BrickSetTagList
|
from .set_tag_list import BrickSetTagList
|
||||||
from .set import BrickSet
|
from .set import BrickSet
|
||||||
|
|
||||||
@ -24,6 +25,7 @@ class BrickSetList(BrickRecordList[BrickSet]):
|
|||||||
select_query: str = 'set/list/all'
|
select_query: str = 'set/list/all'
|
||||||
using_minifigure_query: str = 'set/list/using_minifigure'
|
using_minifigure_query: str = 'set/list/using_minifigure'
|
||||||
using_part_query: str = 'set/list/using_part'
|
using_part_query: str = 'set/list/using_part'
|
||||||
|
using_storage_query: str = 'set/list/using_storage'
|
||||||
|
|
||||||
def __init__(self, /):
|
def __init__(self, /):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -151,3 +153,13 @@ class BrickSetList(BrickRecordList[BrickSet]):
|
|||||||
self.list(override_query=self.using_part_query)
|
self.list(override_query=self.using_part_query)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
# Sets using a storage
|
||||||
|
def using_storage(self, storage: BrickSetStorage, /) -> Self:
|
||||||
|
# Save the parameters to the fields
|
||||||
|
self.fields.storage = storage.fields.id
|
||||||
|
|
||||||
|
# Load the sets from the database
|
||||||
|
self.list(override_query=self.using_storage_query)
|
||||||
|
|
||||||
|
return self
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
from .metadata import BrickMetadata
|
from .metadata import BrickMetadata
|
||||||
|
|
||||||
|
from flask import url_for
|
||||||
|
|
||||||
|
|
||||||
# Lego set storage metadata
|
# Lego set storage metadata
|
||||||
class BrickSetStorage(BrickMetadata):
|
class BrickSetStorage(BrickMetadata):
|
||||||
@ -11,3 +13,10 @@ class BrickSetStorage(BrickMetadata):
|
|||||||
select_query: str = 'set/metadata/storage/select'
|
select_query: str = 'set/metadata/storage/select'
|
||||||
update_field_query: str = 'set/metadata/storage/update/field'
|
update_field_query: str = 'set/metadata/storage/update/field'
|
||||||
update_set_state_query: str = 'set/metadata/storage/update/state'
|
update_set_state_query: str = 'set/metadata/storage/update/state'
|
||||||
|
|
||||||
|
# Self url
|
||||||
|
def url(self, /) -> str:
|
||||||
|
return url_for(
|
||||||
|
'storage.details',
|
||||||
|
id=self.fields.id,
|
||||||
|
)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Self
|
from typing import Self
|
||||||
|
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
from .metadata_list import BrickMetadataList
|
from .metadata_list import BrickMetadataList
|
||||||
from .set_storage import BrickSetStorage
|
from .set_storage import BrickSetStorage
|
||||||
|
|
||||||
@ -13,10 +15,25 @@ class BrickSetStorageList(BrickMetadataList[BrickSetStorage]):
|
|||||||
|
|
||||||
# Queries
|
# Queries
|
||||||
select_query = 'set/metadata/storage/list'
|
select_query = 'set/metadata/storage/list'
|
||||||
|
all_query = 'set/metadata/storage/all'
|
||||||
|
|
||||||
# Set state endpoint
|
# Set state endpoint
|
||||||
set_state_endpoint: str = 'set.update_storage'
|
set_state_endpoint: str = 'set.update_storage'
|
||||||
|
|
||||||
|
# Load all storages
|
||||||
|
@classmethod
|
||||||
|
def all(cls, /) -> Self:
|
||||||
|
new = cls.new()
|
||||||
|
new.override()
|
||||||
|
|
||||||
|
for record in new.select(
|
||||||
|
override_query=cls.all_query,
|
||||||
|
order=current_app.config['STORAGE_DEFAULT_ORDER']
|
||||||
|
):
|
||||||
|
new.records.append(new.model(record=record))
|
||||||
|
|
||||||
|
return new
|
||||||
|
|
||||||
# Instantiate the list with the proper class
|
# Instantiate the list with the proper class
|
||||||
@classmethod
|
@classmethod
|
||||||
def new(cls, /, *, force: bool = False) -> Self:
|
def new(cls, /, *, force: bool = False) -> Self:
|
||||||
|
5
bricktracker/sql/set/list/using_storage.sql
Normal file
5
bricktracker/sql/set/list/using_storage.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{% extends 'set/base/full.sql' %}
|
||||||
|
|
||||||
|
{% block where %}
|
||||||
|
WHERE "bricktracker_sets"."storage" IS NOT DISTINCT FROM :storage
|
||||||
|
{% endblock %}
|
14
bricktracker/sql/set/metadata/storage/all.sql
Normal file
14
bricktracker/sql/set/metadata/storage/all.sql
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{% extends 'set/metadata/storage/base.sql' %}
|
||||||
|
|
||||||
|
{% block total_sets %}
|
||||||
|
IFNULL(COUNT("bricktracker_sets"."id"), 0) AS "total_sets"
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block join %}
|
||||||
|
LEFT JOIN "bricktracker_sets"
|
||||||
|
ON "bricktracker_metadata_storages"."id" IS NOT DISTINCT FROM "bricktracker_sets"."storage"
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block group %}
|
||||||
|
GROUP BY "bricktracker_metadata_storages"."id"
|
||||||
|
{% endblock %}
|
@ -1,6 +1,17 @@
|
|||||||
SELECT
|
SELECT
|
||||||
"bricktracker_metadata_storages"."id",
|
"bricktracker_metadata_storages"."id",
|
||||||
"bricktracker_metadata_storages"."name"
|
"bricktracker_metadata_storages"."name",
|
||||||
|
{% block total_sets %}
|
||||||
|
NULL as "total_sets" -- dummy for order: total_sets
|
||||||
|
{% endblock %}
|
||||||
FROM "bricktracker_metadata_storages"
|
FROM "bricktracker_metadata_storages"
|
||||||
|
|
||||||
|
{% block join %}{% endblock %}
|
||||||
|
|
||||||
{% block where %}{% endblock %}
|
{% block where %}{% endblock %}
|
||||||
|
|
||||||
|
{% block group %}{% endblock %}
|
||||||
|
|
||||||
|
{% if order %}
|
||||||
|
ORDER BY {{ order }}
|
||||||
|
{% endif %}
|
||||||
|
36
bricktracker/views/storage.py
Normal file
36
bricktracker/views/storage.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from flask import Blueprint, render_template
|
||||||
|
|
||||||
|
from .exceptions import exception_handler
|
||||||
|
from ..set_owner_list import BrickSetOwnerList
|
||||||
|
from ..set_list import BrickSetList
|
||||||
|
from ..set_storage import BrickSetStorage
|
||||||
|
from ..set_storage_list import BrickSetStorageList
|
||||||
|
from ..set_tag_list import BrickSetTagList
|
||||||
|
|
||||||
|
storage_page = Blueprint('storage', __name__, url_prefix='/storages')
|
||||||
|
|
||||||
|
|
||||||
|
# Index
|
||||||
|
@storage_page.route('/', methods=['GET'])
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def list() -> str:
|
||||||
|
return render_template(
|
||||||
|
'storages.html',
|
||||||
|
table_collection=BrickSetStorageList.all(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Storage details
|
||||||
|
@storage_page.route('/<id>/details')
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def details(*, id: str) -> str:
|
||||||
|
storage = BrickSetStorage().select_specific(id)
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
'storage.html',
|
||||||
|
item=storage,
|
||||||
|
sets=BrickSetList().using_storage(storage),
|
||||||
|
brickset_owners=BrickSetOwnerList.list(),
|
||||||
|
brickset_storages=BrickSetStorageList.list(as_class=True),
|
||||||
|
brickset_tags=BrickSetTagList.list(),
|
||||||
|
)
|
@ -26,10 +26,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro cards(card_collection, title, id, parent, target, icon=none) %}
|
{% macro cards(card_collection, title, id, parent, target, expanded=false, icon=none) %}
|
||||||
{% set size=card_collection | length %}
|
{% set size=card_collection | length %}
|
||||||
{% if size %}
|
{% if size %}
|
||||||
{{ header(title, id, parent, icon=icon) }}
|
{{ header(title, id, parent, expanded=expanded, icon=icon) }}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% for item in card_collection %}
|
{% for item in card_collection %}
|
||||||
<div class="col-md-6 col-xl-3 d-flex align-items-stretch">
|
<div class="col-md-6 col-xl-3 d-flex align-items-stretch">
|
||||||
|
@ -80,7 +80,7 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
{% set text=storage.fields.name %}
|
{% set text=storage.fields.name %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ badge(check=storage, solo=solo, last=last, color='light text-warning-emphasis bg-warning-subtle border border-warning-subtle', icon='archive-2-line', text=text, alt='Storage', tooltip=tooltip) }}
|
{{ badge(url=item.url_for_storage(), solo=solo, last=last, color='light text-warning-emphasis bg-warning-subtle border border-warning-subtle', icon='archive-2-line', text=text, alt='Storage', tooltip=tooltip) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
{% macro header(color=false, parts=false, quantity=false, missing_parts=false, damaged_parts=false, sets=false, minifigures=false) %}
|
{% macro header(image=true, color=false, parts=false, quantity=false, missing=true, missing_parts=false, damaged=true, damaged_parts=false, sets=false, minifigures=false) %}
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th data-table-no-sort="true" class="no-sort" scope="col"><i class="ri-image-line fw-normal"></i> Image</th>
|
{% if image %}
|
||||||
|
<th data-table-no-sort="true" class="no-sort" scope="col"><i class="ri-image-line fw-normal"></i> Image</th>
|
||||||
|
{% endif %}
|
||||||
<th scope="col"><i class="ri-pencil-line fw-normal"></i> Name</th>
|
<th scope="col"><i class="ri-pencil-line fw-normal"></i> Name</th>
|
||||||
{% if color %}
|
{% if color %}
|
||||||
<th scope="col"><i class="ri-palette-line fw-normal"></i> Color</th>
|
<th scope="col"><i class="ri-palette-line fw-normal"></i> Color</th>
|
||||||
@ -12,10 +14,10 @@
|
|||||||
{% if quantity %}
|
{% if quantity %}
|
||||||
<th data-table-number="true" scope="col"><i class="ri-functions fw-normal"></i> Quantity</th>
|
<th data-table-number="true" scope="col"><i class="ri-functions fw-normal"></i> Quantity</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not config['HIDE_TABLE_MISSING_PARTS'] %}
|
{% if missing and not config['HIDE_TABLE_MISSING_PARTS'] %}
|
||||||
<th data-table-number="true" scope="col"><i class="ri-question-line fw-normal"></i> Missing{% if missing_parts %} parts{% endif %}</th>
|
<th data-table-number="true" scope="col"><i class="ri-question-line fw-normal"></i> Missing{% if missing_parts %} parts{% endif %}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not config['HIDE_TABLE_DAMAGED_PARTS'] %}
|
{% if damaged and not config['HIDE_TABLE_DAMAGED_PARTS'] %}
|
||||||
<th data-table-number="true" scope="col"><i class="ri-error-warning-line fw-normal"></i> Damaged{% if damaged_parts %} parts{% endif %}</th>
|
<th data-table-number="true" scope="col"><i class="ri-error-warning-line fw-normal"></i> Damaged{% if damaged_parts %} parts{% endif %}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if sets %}
|
{% if sets %}
|
||||||
|
15
templates/storage.html
Normal file
15
templates/storage.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block title %} - Storage {{ item.fields.name}}{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
{% with solo=true %}
|
||||||
|
{% include 'storage/card.html' %}
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
16
templates/storage/card.html
Normal file
16
templates/storage/card.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{% import 'macro/accordion.html' as accordion with context %}
|
||||||
|
{% import 'macro/badge.html' as badge %}
|
||||||
|
{% import 'macro/card.html' as card %}
|
||||||
|
|
||||||
|
<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=last) }}
|
||||||
|
</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') }}
|
||||||
|
</div>
|
||||||
|
<div class="card-footer"></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
16
templates/storage/table.html
Normal file
16
templates/storage/table.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{% import 'macro/form.html' as form %}
|
||||||
|
{% import 'macro/table.html' as table %}
|
||||||
|
|
||||||
|
<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) }}
|
||||||
|
<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>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
11
templates/storages.html
Normal file
11
templates/storages.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block title %} - All storages{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<div class="container-fluid px-0">
|
||||||
|
{% with all=true %}
|
||||||
|
{% include 'storage/table.html' %}
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user