feat(sets): added notes field to sets. Displayed both at top of page, if not empty and in the metadata section, where it can be changed
This commit is contained in:
@@ -30,6 +30,7 @@ class BrickSet(RebrickableSet):
|
|||||||
insert_query: str = 'set/insert'
|
insert_query: str = 'set/insert'
|
||||||
update_purchase_date_query: str = 'set/update/purchase_date'
|
update_purchase_date_query: str = 'set/update/purchase_date'
|
||||||
update_purchase_price_query: str = 'set/update/purchase_price'
|
update_purchase_price_query: str = 'set/update/purchase_price'
|
||||||
|
update_description_query: str = 'set/update/description'
|
||||||
|
|
||||||
# Delete a set
|
# Delete a set
|
||||||
def delete(self, /) -> None:
|
def delete(self, /) -> None:
|
||||||
@@ -370,3 +371,36 @@ class BrickSet(RebrickableSet):
|
|||||||
# Update purchase price url
|
# Update purchase price url
|
||||||
def url_for_purchase_price(self, /) -> str:
|
def url_for_purchase_price(self, /) -> str:
|
||||||
return url_for('set.update_purchase_price', id=self.fields.id)
|
return url_for('set.update_purchase_price', id=self.fields.id)
|
||||||
|
|
||||||
|
# Update description
|
||||||
|
def update_description(self, json: Any | None, /) -> Any:
|
||||||
|
value = json.get('value', None) # type: ignore
|
||||||
|
|
||||||
|
if value == '':
|
||||||
|
value = None
|
||||||
|
|
||||||
|
self.fields.description = value
|
||||||
|
|
||||||
|
rows, _ = BrickSQL().execute_and_commit(
|
||||||
|
self.update_description_query,
|
||||||
|
parameters=self.sql_parameters()
|
||||||
|
)
|
||||||
|
|
||||||
|
if rows != 1:
|
||||||
|
raise DatabaseException('Could not update the description for set {set} ({id})'.format( # noqa: E501
|
||||||
|
set=self.fields.set,
|
||||||
|
id=self.fields.id,
|
||||||
|
))
|
||||||
|
|
||||||
|
# Info
|
||||||
|
logger.info('Description changed to "{value}" for set {set} ({id})'.format( # noqa: E501
|
||||||
|
value=value,
|
||||||
|
set=self.fields.set,
|
||||||
|
id=self.fields.id,
|
||||||
|
))
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
# Update description url
|
||||||
|
def url_for_description(self, /) -> str:
|
||||||
|
return url_for('set.update_description', id=self.fields.id)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ SELECT
|
|||||||
"bricktracker_sets"."purchase_date",
|
"bricktracker_sets"."purchase_date",
|
||||||
"bricktracker_sets"."purchase_location",
|
"bricktracker_sets"."purchase_location",
|
||||||
"bricktracker_sets"."purchase_price",
|
"bricktracker_sets"."purchase_price",
|
||||||
|
"bricktracker_sets"."description",
|
||||||
"rebrickable_sets"."set",
|
"rebrickable_sets"."set",
|
||||||
"rebrickable_sets"."number",
|
"rebrickable_sets"."number",
|
||||||
"rebrickable_sets"."version",
|
"rebrickable_sets"."version",
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
UPDATE "bricktracker_sets"
|
||||||
|
SET "description" = :description
|
||||||
|
WHERE "bricktracker_sets"."id" IS NOT DISTINCT FROM :id
|
||||||
@@ -158,6 +158,18 @@ def update_purchase_price(*, id: str) -> Response:
|
|||||||
return jsonify({'value': value})
|
return jsonify({'value': value})
|
||||||
|
|
||||||
|
|
||||||
|
# Change the value of description
|
||||||
|
@set_page.route('/<id>/description', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__, json=True)
|
||||||
|
def update_description(*, id: str) -> Response:
|
||||||
|
brickset = BrickSet().select_light(id)
|
||||||
|
|
||||||
|
value = brickset.update_description(request.json)
|
||||||
|
|
||||||
|
return jsonify({'value': value})
|
||||||
|
|
||||||
|
|
||||||
# Change the state of a owner
|
# Change the state of a owner
|
||||||
@set_page.route('/<id>/owner/<metadata_id>', methods=['POST'])
|
@set_page.route('/<id>/owner/<metadata_id>', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ class BrickChanger {
|
|||||||
listener = "change";
|
listener = "change";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "TEXTAREA":
|
||||||
|
this.html_type = "textarea";
|
||||||
|
listener = "change";
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw Error(`Unsupported HTML tag type for BrickChanger: ${this.html_element.tagName}`);
|
throw Error(`Unsupported HTML tag type for BrickChanger: ${this.html_element.tagName}`);
|
||||||
}
|
}
|
||||||
@@ -131,6 +136,7 @@ class BrickChanger {
|
|||||||
|
|
||||||
case "text":
|
case "text":
|
||||||
case "select":
|
case "select":
|
||||||
|
case "textarea":
|
||||||
value = this.html_element.value;
|
value = this.html_element.value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,30 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro textarea(name, id, prefix, url, value, all=none, read_only=none, icon=none, rows=3, delete=false) %}
|
||||||
|
{% if all or read_only %}
|
||||||
|
{{ value }}
|
||||||
|
{% else %}
|
||||||
|
<label class="visually-hidden" for="{{ prefix }}-{{ id }}">{{ name }}</label>
|
||||||
|
<div class="input-group">
|
||||||
|
{% if icon %}<span class="input-group-text px-1"><i class="ri-{{ icon }} me-1"></i><span class="ms-1 d-none d-md-inline"> {{ name }}</span></span>{% endif %}
|
||||||
|
<textarea class="form-control form-control-sm flex-shrink-1 px-1" id="{{ prefix }}-{{ id }}" rows="{{ rows }}"
|
||||||
|
{% if g.login.is_authenticated() and not delete %}
|
||||||
|
data-changer-id="{{ id }}" data-changer-prefix="{{ prefix }}" data-changer-url="{{ url }}"
|
||||||
|
{% else %}
|
||||||
|
disabled
|
||||||
|
{% endif %}
|
||||||
|
autocomplete="off">{% if value %}{{ value }}{% endif %}</textarea>
|
||||||
|
{% if g.login.is_authenticated() and not delete %}
|
||||||
|
<span id="status-{{ prefix }}-{{ id }}" class="input-group-text ri-save-line px-1"></span>
|
||||||
|
<button id="clear-{{ prefix }}-{{ id }}" type="button" class="btn btn-sm btn-light btn-outline-danger border px-1"><i class="ri-eraser-line"></i></button>
|
||||||
|
{% else %}
|
||||||
|
<span class="input-group-text ri-prohibited-line px-1"></span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro filter_toggle(filter_id) %}
|
{% macro filter_toggle(filter_id) %}
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-outline-secondary filter-toggle"
|
class="btn btn-outline-secondary filter-toggle"
|
||||||
|
|||||||
@@ -60,6 +60,13 @@
|
|||||||
{# Render badges in configured order based on context (grid view vs detail view) #}
|
{# 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') }}
|
{{ 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>
|
</div>
|
||||||
|
{% if item.fields.description %}
|
||||||
|
<div class="card-body border-bottom-0 {% if not solo %}p-1{% endif %} pt-0"{% if current_viewing %} style="border-color: var(--bs-border-color) !important; border-width: 1px !important;"{% endif %}>
|
||||||
|
<div class="alert alert-info mb-0 {% if not solo %}p-1 small{% endif %}" role="alert">
|
||||||
|
<i class="ri-sticky-note-line me-1"></i>{{ item.fields.description }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% if not tiny and brickset_statuses | length %}
|
{% 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 %}>
|
<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 %}>
|
||||||
{% for status in brickset_statuses %}
|
{% for status in brickset_statuses %}
|
||||||
|
|||||||
@@ -34,6 +34,9 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<a href="{{ url_for('admin.admin', open_purchase_location=true) }}" class="btn btn-primary" role="button"><i class="ri-settings-4-line"></i> Manage the set purchase locations</a>
|
<a href="{{ url_for('admin.admin', open_purchase_location=true) }}" class="btn btn-primary" role="button"><i class="ri-settings-4-line"></i> Manage the set purchase locations</a>
|
||||||
{{ accordion.footer() }}
|
{{ accordion.footer() }}
|
||||||
|
{{ accordion.header('Notes', 'notes', 'set-management', icon='sticky-note-line') }}
|
||||||
|
{{ form.textarea('Notes', item.fields.id, 'description', item.url_for_description(), item.fields.description, rows=4, delete=delete) }}
|
||||||
|
{{ accordion.footer() }}
|
||||||
{{ accordion.header('Storage', 'storage', 'set-management', icon='archive-2-line') }}
|
{{ accordion.header('Storage', 'storage', 'set-management', icon='archive-2-line') }}
|
||||||
{% if brickset_storages | length %}
|
{% if brickset_storages | length %}
|
||||||
{{ form.select('Storage', item.fields.id, brickset_storages.as_prefix(), brickset_storages.url_for_set_value(item.fields.id), item.fields.storage, brickset_storages, icon='building-line', delete=delete) }}
|
{{ form.select('Storage', item.fields.id, brickset_storages.as_prefix(), brickset_storages.url_for_set_value(item.fields.id), item.fields.storage, brickset_storages, icon='building-line', delete=delete) }}
|
||||||
|
|||||||
Reference in New Issue
Block a user