Add filtering functionality to inventory listing and enhance templates for better data presentation

This commit is contained in:
Yaro Kasear 2025-06-12 11:29:18 -05:00
parent a7708ce9c5
commit 5df5f86fd2
8 changed files with 64 additions and 9 deletions

Binary file not shown.

Binary file not shown.

View file

@ -4,7 +4,7 @@ import html
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from typing import Callable, Any, List from typing import Callable, Any, List
from . import db from . import db
from .utils import eager_load_user_relationships, eager_load_inventory_relationships, eager_load_room_relationships, eager_load_worklog_relationships from .utils import eager_load_user_relationships, eager_load_inventory_relationships, eager_load_room_relationships, eager_load_worklog_relationships, chunk_list
main = Blueprint('main', __name__) main = Blueprint('main', __name__)
@ -106,7 +106,8 @@ def render_paginated_table(
entry_route: str, entry_route: str,
row_fn: Callable[[Any], List[dict]], row_fn: Callable[[Any], List[dict]],
endpoint: str, endpoint: str,
per_page=15 per_page=15,
extra_args={}
): ):
data = make_paginated_data(query, page, per_page) data = make_paginated_data(query, page, per_page)
return render_template( return render_template(
@ -120,7 +121,8 @@ def render_paginated_table(
endpoint=endpoint, endpoint=endpoint,
total_pages=data['total_pages'], total_pages=data['total_pages'],
headers=headers, headers=headers,
entry_route=entry_route entry_route=entry_route,
extra_args=extra_args
) )
@main.route("/") @main.route("/")
@ -130,24 +132,56 @@ def index():
def link(text, endpoint, **values): def link(text, endpoint, **values):
return {"text": text, "url": url_for(endpoint, **values)} return {"text": text, "url": url_for(endpoint, **values)}
FILTER_MAP = {
'user': Inventory.owner_id,
'room': Inventory.location_id,
'brand': Inventory.brand_id,
}
@main.route("/inventory") @main.route("/inventory")
def list_inventory(): def list_inventory():
page = request.args.get('page', default=1, type=int) page = request.args.get('page', default=1, type=int)
query = eager_load_inventory_relationships(db.session.query(Inventory)).order_by(Inventory.inventory_name, Inventory.barcode, Inventory.serial) filter_by = request.args.get('filter_by', type=str)
id = request.args.get('id', type=int)
query = db.session.query(Inventory)
query = eager_load_inventory_relationships(query)
query = query.order_by(Inventory.inventory_name, Inventory.barcode, Inventory.serial)
if filter_by and id:
column = FILTER_MAP.get(filter_by)
if column is not None:
query = query.filter(column == id)
else:
return "Invalid filter_by parameter", 400
return render_paginated_table( return render_paginated_table(
query=query, query=query,
page=page, page=page,
title="Inventory", title="Inventory (Filtered)" if filter_by else "Inventory",
headers=inventory_headers, headers=inventory_headers,
row_fn=lambda i: [fn(i) for fn in inventory_headers.values()], row_fn=lambda i: [fn(i) for fn in inventory_headers.values()],
endpoint="main.list_inventory", endpoint="main.list_inventory",
entry_route="inventory_item" entry_route="inventory_item",
extra_args={'filter_by': filter_by, 'id': id}
) )
@main.route("/inventory/index") @main.route("/inventory/index")
def inventory_index(): def inventory_index():
category = request.args.get('category') category = request.args.get('category')
return render_template('inventory_index.html', title="Inventory Index", category=category) listing = None
if category == 'user':
users = db.session.query(User.id, User.first_name, User.last_name).order_by(User.first_name, User.last_name).all()
listing = chunk_list([(user.id, f"{user.first_name or ''} {user.last_name or ''}".strip()) for user in users], 12)
elif category == 'location':
pass
elif category == 'type':
pass
elif category:
return f"Dude, why {category}?"
return render_template('inventory_index.html', title="Inventory Index", category=category, listing=listing)
@main.route("/inventory_item/<int:id>") @main.route("/inventory_item/<int:id>")
def inventory_item(id): def inventory_item(id):

View file

@ -1,4 +1,5 @@
{% macro render_table(headers, rows, entry_route=None, title=None) %} {% macro render_table(headers, rows, entry_route=None, title=None) %}
{% if rows %}
<div class="table-responsive"> <div class="table-responsive">
<table <table
class="table table-bordered table-sm table-hover table-striped table-light m-0{% if title %} caption-top{% endif %}"> class="table table-bordered table-sm table-hover table-striped table-light m-0{% if title %} caption-top{% endif %}">
@ -32,6 +33,9 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{% else %}
<div class="container text-center">No data.</div>
{% endif %}
{% endmacro %} {% endmacro %}
{% macro render_pagination(endpoint, page, has_prev, has_next, total_pages, page_variable='page', extra_args={}) %} {% macro render_pagination(endpoint, page, has_prev, has_next, total_pages, page_variable='page', extra_args={}) %}
@ -43,6 +47,7 @@
{% set _ = next_args.update({page_variable: page + 1}) %} {% set _ = next_args.update({page_variable: page + 1}) %}
{% set _ = first_args.update({page_variable: 1}) %} {% set _ = first_args.update({page_variable: 1}) %}
{% set _ = last_args.update({page_variable: total_pages}) %} {% set _ = last_args.update({page_variable: total_pages}) %}
{% if total_pages > 1 %}
<div class="d-flex justify-content-between pt-3 px-5 align-items-center"> <div class="d-flex justify-content-between pt-3 px-5 align-items-center">
<div> <div>
@ -72,4 +77,5 @@
class="btn btn-primary{% if not has_next %} disabled{% endif %}">Last &raquo;</a> class="btn btn-primary{% if not has_next %} disabled{% endif %}">Last &raquo;</a>
</div> </div>
</div> </div>
{% endif %}
{% endmacro %} {% endmacro %}

View file

@ -132,7 +132,7 @@ submit_button=True) }}
total_pages=worklog_pagination['total_pages'], total_pages=worklog_pagination['total_pages'],
endpoint='main.inventory_item', endpoint='main.inventory_item',
page_variable='worklog_page', page_variable='worklog_page',
extra_args={'id': item.id, 'worklog_page': worklog_page} extra_args={'id': item.id, 'worklog_page': worklog_page, 'filter_by': filter_by, 'id': id}
) }} ) }}
{% endif %} {% endif %}
</div> </div>

View file

@ -77,6 +77,18 @@ title=title
By Type By Type
</a></div> </a></div>
</div> </div>
{% else %}
<div class="container">
{% for line in listing %}
<div class="row my-3">
{% for id, name in line %}
<div class="col text-center">
<a href="{{ url_for('main.list_inventory', filter_by='user', id=id) }}" class="link-success link-underline-opacity-0">{{ name }}</a>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View file

@ -13,5 +13,5 @@
) }} ) }}
{{ tables.render_table(header, rows, entry_route) }} {{ tables.render_table(header, rows, entry_route) }}
{{ tables.render_pagination(endpoint, page, has_prev, has_next, total_pages) }} {{ tables.render_pagination(endpoint, page, has_prev, has_next, total_pages, extra_args=extra_args) }}
{% endblock %} {% endblock %}

View file

@ -28,3 +28,6 @@ def eager_load_worklog_relationships(query):
joinedload(WorkLog.contact), joinedload(WorkLog.contact),
joinedload(WorkLog.work_item) joinedload(WorkLog.work_item)
) )
def chunk_list(lst, chunk_size):
return [lst[i:i + chunk_size] for i in range(0, len(lst), chunk_size)]