Refactor templates and utility functions for improved rendering and usability; enhance icon usage and link handling in various templates

This commit is contained in:
Yaro Kasear 2025-06-17 12:51:01 -05:00
parent b68c25a05a
commit 3915b97231
12 changed files with 102 additions and 90 deletions

View file

@ -308,8 +308,6 @@ def list_users():
@main.route("/user/<int:id>")
def user(id):
asset_page = request.args.get("asset_page", default=1, type=int)
worklog_page = request.args.get("worklog_page", default=1, type=int)
users_query = db.session.query(User).order_by(User.first_name, User.last_name)
users = eager_load_user_relationships(users_query).all()
user = next((u for u in users if u.id == id), None)
@ -417,4 +415,4 @@ def search():
}
}
return render_template('search.html', title="Database Search", results=results, query=query)
return render_template('search.html', title=f"Database Search ({query})" if query else "Database Search", results=results, query=query)

View file

@ -1,13 +1,12 @@
{% import "fragments/_icon_fragment.html" as icons %}
{% macro breadcrumb_header(breadcrumbs=[], title=None, submit_button=False) %}
<nav class="row d-flex mb-3 justify-content-between">
<div class="col">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="{{ url_for('index') }}" class="link-success link-underline-opacity-0">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-house" viewBox="0 0 16 16">
<path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L2 8.207V13.5A1.5 1.5 0 0 0 3.5 15h9a1.5 1.5 0 0 0 1.5-1.5V8.207l.646.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293zM13 7.207V13.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5V7.207l5-5z"/>
</svg>
{{ icons.home(16) }}
</a>
</li>
{% for crumb in breadcrumbs %}

View file

@ -1,67 +1,85 @@
{% set inventory %}
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor"
{% macro home(size=24) %}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" fill="currentColor"
class="bi bi-house align-self-center" viewBox="0 0 16 16">
<path
d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L2 8.207V13.5A1.5 1.5 0 0 0 3.5 15h9a1.5 1.5 0 0 0 1.5-1.5V8.207l.646.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293zM13 7.207V13.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5V7.207l5-5z" />
</svg>
{% endmacro %}
{% macro inventory(size=24) %}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" fill="currentColor"
class="bi bi-laptop align-self-center" viewBox="0 0 16 16">
<path
d="M13.5 3a.5.5 0 0 1 .5.5V11H2V3.5a.5.5 0 0 1 .5-.5zm-11-1A1.5 1.5 0 0 0 1 3.5V12h14V3.5A1.5 1.5 0 0 0 13.5 2zM0 12.5h16a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 12.5" />
</svg>
{% endset %}
{% endmacro %}
{% set laptop %}
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor"
{% macro laptop(size=24) %}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" fill="currentColor"
class="bi bi-laptop align-self-center" viewBox="0 0 16 16">
<path
d="M13.5 3a.5.5 0 0 1 .5.5V11H2V3.5a.5.5 0 0 1 .5-.5zm-11-1A1.5 1.5 0 0 0 1 3.5V12h14V3.5A1.5 1.5 0 0 0 13.5 2zM0 12.5h16a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 12.5" />
</svg>
{% endset %}
{% endmacro %}
{% set log %}
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor"
{% macro link(size=24) %}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" fill="currentColor" class="bi bi-box-arrow-up-right align-self-center"
viewBox="0 0 16 16">
<path fill-rule="evenodd"
d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5" />
<path fill-rule="evenodd"
d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0z" />
</svg>
{% endmacro %}
{% macro log(size=24) %}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" fill="currentColor"
class="bi bi-file-text align-self-center" viewBox="0 0 16 16">
<path
d="M5 4a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1zm-.5 2.5A.5.5 0 0 1 5 6h6a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5M5 8a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1zm0 2a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1z" />
<path
d="M2 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2zm10-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1" />
</svg>
{% endset %}
{% endmacro %}
{% set map %}
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-map align-self-center"
viewBox="0 0 16 16">
{% macro map(size=24) %}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" fill="currentColor"
class="bi bi-map align-self-center" viewBox="0 0 16 16">
<path fill-rule="evenodd"
d="M15.817.113A.5.5 0 0 1 16 .5v14a.5.5 0 0 1-.402.49l-5 1a.5.5 0 0 1-.196 0L5.5 15.01l-4.902.98A.5.5 0 0 1 0 15.5v-14a.5.5 0 0 1 .402-.49l5-1a.5.5 0 0 1 .196 0L10.5.99l4.902-.98a.5.5 0 0 1 .415.103M10 1.91l-4-.8v12.98l4 .8zm1 12.98 4-.8V1.11l-4 .8zm-6-.8V1.11l-4 .8v12.98z" />
</svg>
{% endset %}
{% endmacro %}
{% set motherboard %}
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor"
{% macro motherboard(size=24) %}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" fill="currentColor"
class="bi bi-motherboard align-self-center" viewBox="0 0 16 16">
<path
d="M11.5 2a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m2 0a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5m-10 8a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1zm0 2a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1zM5 3a1 1 0 0 0-1 1h-.5a.5.5 0 0 0 0 1H4v1h-.5a.5.5 0 0 0 0 1H4a1 1 0 0 0 1 1v.5a.5.5 0 0 0 1 0V8h1v.5a.5.5 0 0 0 1 0V8a1 1 0 0 0 1-1h.5a.5.5 0 0 0 0-1H9V5h.5a.5.5 0 0 0 0-1H9a1 1 0 0 0-1-1v-.5a.5.5 0 0 0-1 0V3H6v-.5a.5.5 0 0 0-1 0zm0 1h3v3H5zm6.5 7a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h2a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5z" />
<path
d="M1 2a2 2 0 0 1 2-2h11a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-2H.5a.5.5 0 0 1-.5-.5v-1A.5.5 0 0 1 .5 9H1V8H.5a.5.5 0 0 1-.5-.5v-1A.5.5 0 0 1 .5 6H1V5H.5a.5.5 0 0 1-.5-.5v-2A.5.5 0 0 1 .5 2zm1 11a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1z" />
</svg>
{% endset %}
{% endmacro %}
{% set search %}
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor"
{% macro search(size=24) %}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" fill="currentColor"
class="bi bi-search align-self-center" viewBox="0 0 16 16">
<path
d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0" />
</svg>
{% endset %}
{% endmacro %}
{% set table %}
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-table align-self-center"
viewBox="0 0 16 16">
{% macro table(size=24) %}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" fill="currentColor"
class="bi bi-table align-self-center" viewBox="0 0 16 16">
<path
d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm15 2h-4v3h4zm0 4h-4v3h4zm0 4h-4v3h3a1 1 0 0 0 1-1zm-5 3v-3H6v3zm-5 0v-3H1v2a1 1 0 0 0 1 1zm-4-4h4V8H1zm0-4h4V4H1zm5-3v3h4V4zm4 4H6v3h4z" />
</svg>
{% endset %}
{% endmacro %}
{% set user %}
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor"
{% macro user(size=24) %}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" fill="currentColor"
class="bi bi-person align-self-center" viewBox="0 0 16 16">
<path
d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6m2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0m4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4m-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10s-3.516.68-4.168 1.332c-.678.678-.83 1.418-.832 1.664z" />
</svg>
{% endset %}
{% endmacro %}

View file

@ -1,3 +1,5 @@
{% import "fragments/_icon_fragment.html" as icons %}
{% macro category_link(endpoint, label, icon_html=none, arguments={}) %}
<div class="col text-center">
<a href="{{ url_for('main.' + endpoint, **arguments) }}"
@ -20,3 +22,9 @@
</a>
</li>
{% endmacro %}
{% macro entry_link(endpoint, id) %}
<a href="{{ url_for('main.' + endpoint, id=id) }}" class="link-success link-underline-opacity-0">
{{ icons.link(12) }}
</a>
{% endmacro %}

View file

@ -9,7 +9,7 @@
<thead class="sticky-top">
<tr>
{% for h in headers %}
<th>{{ h }}</th>
<th class="text-nowrap">{{ h }}</th>
{% endfor %}
</tr>
</thead>
@ -18,7 +18,7 @@
<tr {% if entry_route %}onclick="window.location='{{ url_for('main.' + entry_route, id=row.id) }}'"
style="cursor: pointer;" {% endif %}>
{% for cell in row.cells %}
<td {% if cell.type=='bool' %}class="text-center" {% endif %}>
<td class="text-nowrap{% if cell.type=='bool' %} text-center{% endif %}">
{% if cell.type == 'bool' %}
{{ cell.html | safe }}
{% elif cell.url %}

View file

@ -61,7 +61,8 @@ submit_button=True) }}
<select name="type" id="type" class="form-select">
<option>-</option>
{% for t in types %}
<option value="{{ t.id }}"{% if t.id == item.type_id %} selected{% endif %}>{{ t.description }}</option>
<option value="{{ t.id }}" {% if t.id==item.type_id %} selected{% endif %}>{{ t.description }}
</option>
{% endfor %}
</select>
</div>
@ -70,16 +71,9 @@ submit_button=True) }}
<div class="col-4">
<label for="owner" class="form-label">
Contact
<a href="{{ url_for('main.user', id=item.owner_id) }}"
class="link-success link-underline-opacity-0">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor"
class="bi bi-box-arrow-up-right" viewBox="0 0 16 16">
<path fill-rule="evenodd"
d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5" />
<path fill-rule="evenodd"
d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0z" />
</svg>
</a>
{% if item.owner %}
{{ links.entry_link('user', item.owner_id) }}
{% endif %}
</label>
<select class="form-select" id="userList">
<option>-</option>
@ -117,14 +111,15 @@ submit_button=True) }}
</div>
</div>
<div class="row">
<div class="col-{% if worklog %}6{% else %}12{% endif %}">
<div class="col">
<label for="notes" class="form-label">Notes &amp; Comments</label>
<textarea name="notes" id="notes" class="form-control"
rows="10">{{ item.notes if item.notes else '' }}</textarea>
</div>
{% if worklog %}
<div class="col-6">
{{ tables.render_table(headers=worklog_headers, rows=worklog_rows, id='worklog', entry_route='worklog_entry', title='Work Log') }}
<div class="col">
{{ tables.render_table(headers=worklog_headers, rows=worklog_rows, id='worklog',
entry_route='worklog_entry', title='Work Log') }}
</div>
{% endif %}
</div>

View file

@ -16,8 +16,8 @@ title=title
</div>
</div>
<div class="row text-center">
{{ links.category_link(endpoint = 'search', label = 'Search', icon_html = icons.search) }}
{{ links.category_link(endpoint = 'list_inventory', label = "List", icon_html = icons.table) }}
{{ links.category_link(endpoint = 'search', label = 'Search', icon_html = icons.search(32)) }}
{{ links.category_link(endpoint = 'list_inventory', label = "List", icon_html = icons.table(32)) }}
</div>
<div class="row">
<div class="col">
@ -25,9 +25,9 @@ title=title
</div>
</div>
<div class="row text-center">
{{ links.category_link(endpoint = 'inventory_index', label = "By User", icon_html = icons.user, arguments = {'category': 'user'}) }}
{{ links.category_link(endpoint = 'inventory_index', label = 'By Location', icon_html = icons.map, arguments = {'category': 'location'}) }}
{{ links.category_link(endpoint = 'inventory_index', label = 'By Type', icon_html = icons.motherboard, arguments = {'category': 'type'}) }}
{{ links.category_link(endpoint = 'inventory_index', label = "By User", icon_html = icons.user(32), arguments = {'category': 'user'}) }}
{{ links.category_link(endpoint = 'inventory_index', label = 'By Location', icon_html = icons.map(32), arguments = {'category': 'location'}) }}
{{ links.category_link(endpoint = 'inventory_index', label = 'By Type', icon_html = icons.motherboard(32), arguments = {'category': 'type'}) }}
</div>
{% else %}
<div class="container">

View file

@ -19,11 +19,6 @@
crossorigin="anonymous">
<style>
{% block style %}
table td,
th {
white-space: nowrap;
}
input[type="checkbox"][disabled] {
pointer-events: none;
opacity: 1;

View file

@ -43,11 +43,14 @@ title=title,
)}}
</div>
{% endif %}
{% if not results['inventory']['rows'] and not results['users']['rows'] and not results['worklog']['rows'] %}
<div>There are no results for "{{ query }}".</div>
{% endif %}
</div>
{% endblock %}
{% block script %}
{% if query %}
{% if query and (results['inventory']['rows'] or results['users']['rows'] or results['worklog']['rows']) %}
const query = "{{ query|e }}";
if (query) {
const instance = new Mark(document.querySelector("main"));

View file

@ -35,16 +35,7 @@ breadcrumbs=[
<label for="supervisor" class="form-label">
Supervisor
{% if user.supervisor %}
<a href="{{ url_for('main.user', id=user.supervisor_id) }}"
class="link-success link-underline-opacity-0">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor"
class="bi bi-box-arrow-up-right" viewBox="0 0 16 16">
<path fill-rule="evenodd"
d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5" />
<path fill-rule="evenodd"
d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0z" />
</svg>
</a>
{{ links.entry_link('user', user.supervisor_id) }}
{% endif %}
</label>
<select class="form-select" id="supervisor" name="supervisor"
@ -81,16 +72,16 @@ breadcrumbs=[
</form>
<div class="row mt-3">
{% if inventory_rows %}
<div class="col{% if user.worklog_rows %}-6{% endif %}">
<div class="col">
<div class="row">
{{ tables.render_table(inventory_headers, inventory_rows, 'inventory_item', title='Assets', per_page=8) }}
{{ tables.render_table(headers=inventory_headers, rows=inventory_rows, id='assets', entry_route='inventory_item', title='Assets', per_page=8) }}
</div>
</div>
{% endif %}
{% if worklog_rows %}
<div class="col{% if user.inventory_rows %}-6{% endif %}">
<div class="col">
<div class="row">
{{ tables.render_table(worklog_headers, worklog_rows, 'worklog_entry', title='Work Done', per_page=8) }}
{{ tables.render_table(headers=worklog_headers, rows=worklog_rows, id='worklog', entry_route='worklog_entry', title='Work Done', per_page=8) }}
</div>
</div>
{% endif %}

View file

@ -29,28 +29,33 @@
</div>
<div class="row">
<div class="col-4">
<label for="contact" class="form-label">Contact</label>
<input list="contactList" class="form-control" id="contact" value="{{ log.contact.full_name }}"
data-datalist-bind="#contactList" data-hidden-target="#contactId">
<input type="hidden" id="contactId">
<datalist id="contactList">
<label for="contact" class="form-label">
Contact
{% if log.contact_id %}
{{ links.entry_link('user', log.contact_id) }}
{% endif %}
</label>
<select class="form-select" name="contact" id="contact">
<option>-</option>
{% for contact in users %}
<option data-id="{{ contact.id }}" value="{{ contact.full_name }}">{{ contact.full_name }}
<option data-id="{{ contact.id }}" value="{{ contact.full_name }}"{% if contact.id == log.contact_id %} selected{% endif %}>{{ contact.full_name }}
</option>
{% endfor %}
</datalist>
</select>
</div>
<div class="col-4">
<label for="item" class="form-label">Work Item</label>
<input list="itemList" class="form-control" id="item" placeholder="-"
value="{{ log.work_item.identifier }}" data-datalist-bind="#itemList"
data-hidden-target="#itemId">
<input type="hidden" id="itemId">
<datalist id="itemList">
<label for="item" class="form-label">
Work Item
{% if log.work_item_id %}
{{ links.entry_link('inventory_item', log.work_item_id) }}
{% endif %}
</label>
<select id="item" name="item" class="form-select">
<option>-</option>
{% for item in items %}
<option data-id="{{ item.id }}" value="{{ item.identifier }}">{{ item.identifier }}</option>
<option data-id="{{ item.id }}" value="{{ item.identifier }}"{% if item.id == log.work_item_id %} selected{% endif %}>{{ item.identifier }}</option>
{% endfor %}
</datalist>
</select>
</div>
<div class="col-4">
<div class="row">

View file

@ -12,7 +12,7 @@ def eager_load_inventory_relationships(query):
joinedload(Inventory.owner),
joinedload(Inventory.brand),
joinedload(Inventory.item),
joinedload(Inventory.location).joinedload(Room.room_function)
selectinload(Inventory.location).selectinload(Room.room_function)
)
def eager_load_room_relationships(query):