Refactor worklog handling and rendering; enhance active worklog display and add settings page with brand management functionality

This commit is contained in:
Yaro Kasear 2025-06-18 09:33:33 -05:00
parent 3915b97231
commit e2b8579362
9 changed files with 145 additions and 42 deletions

View file

@ -1,8 +0,0 @@
<!-- templates/worklog.html -->
{% extends "layout.html" %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
{% endblock %}

View file

@ -1,3 +1,13 @@
{% macro gear(size=24) %}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" fill="currentColor"
class="bi bi-gear align-self-center" viewBox="0 0 16 16">
<path
d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492M5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0" />
<path
d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115z" />
</svg>
{% endmacro %}
{% 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">
@ -23,8 +33,8 @@
{% endmacro %}
{% 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">
<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"
@ -50,6 +60,13 @@
</svg>
{% endmacro %}
{% macro minus(size=24) %}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" fill="currentColor"
class="bi bi-dash-lg align-self-center" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M2 8a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11A.5.5 0 0 1 2 8" />
</svg>
{% endmacro %}
{% 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">
@ -60,6 +77,14 @@
</svg>
{% endmacro %}
{% macro plus(size=24) %}
<svg xmlns="http://www.w3.org/2000/svg" width="{{ size }}" height="{{ size }}" fill="currentColor"
class="bi bi-plus-lg align-self-center" viewBox="0 0 16 16">
<path fill-rule="evenodd"
d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2" />
</svg>
{% endmacro %}
{% 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">

View file

@ -16,7 +16,7 @@
<tbody>
{% for row in rows %}
<tr {% if entry_route %}onclick="window.location='{{ url_for('main.' + entry_route, id=row.id) }}'"
style="cursor: pointer;" {% endif %}>
style="cursor: pointer;"{% endif %}{% if row['highlight'] %} class="table-info"{% endif %}>
{% for cell in row.cells %}
<td class="text-nowrap{% if cell.type=='bool' %} text-center{% endif %}">
{% if cell.type == 'bool' %}

View file

@ -8,17 +8,16 @@
<h1 class="display-4">Welcome to Inventory Manager</h1>
<p class="lead">Find out about all of your assets.</p>
<div class="row">
{% if stale_worklog_rows %}
{% if active_worklog_rows %}
<div class="col">
<div class="card">
<div class="card-body">
<h5 class="card-title">Stale Worklogs</h5>
<h6 class="card-subtitle mb-2 text-body-secondary">You have {{ stale_count }} worklogs
that need attention!</h6>
<h5 class="card-title">Active Worklogs</h5>
<h6 class="card-subtitle mb-2 text-body-secondary">You have {{ active_count }} active worklogs.</h6>
{{ tables.render_table(
headers = stale_worklog_headers,
rows = stale_worklog_rows,
id = 'Stale Worklog',
headers = active_worklog_headers,
rows = active_worklog_rows,
id = 'Active Worklog',
entry_route = 'worklog_entry',
per_page = 10
)}}

View file

@ -96,9 +96,10 @@ submit_button=True) }}
<div class="col-2">
<label for="condition" class="form-label">Condition</label>
<select name="condition" id="condition" class="form-select">
<option>-</option>
{% for condition in ["Working", "Deployed", "Partially Inoperable", "Inoperable", "Unverified",
"Removed", "Disposed"] %}
<option value="{{ condition }}">{{ condition }}</option>
<option value="{{ condition }}"{% if item.condition == condition %} selected{% endif %}>{{ condition }}</option>
{% endfor %}
</select>
</div>

View file

@ -10,21 +10,13 @@ title=title
<div class="container">
{% if not category %}
<div class="row">
<div class="col">
<h2 class="display-6 text-center">Find</h2>
</div>
</div>
<div class="row text-center">
{{ 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">
<h2 class="display-6 text-center mt-5">Browse</h2>
</div>
</div>
<div class="row text-center">
{{ links.category_link(endpoint = 'list_inventory', label = "Full Listing", icon_html = icons.table(32)) }}
{{ 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'}) }}

View file

@ -10,7 +10,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}Inventory{% endblock %}</title>
<title>Inventory Manager{% if title %} - {% endif %}{% block title %}{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-4Q6Gf2aSP4eDXB8Miphtr37CMZZQ5oXLH2yaXMJ2w8e2ZtHTl7GptT4jmndRuHDT" crossorigin="anonymous">
<link
@ -55,6 +55,9 @@
<input type="text" class="form-control me-2" placeholder="Search" name="q" id="search" />
<button class="btn btn-primary" type="submit" id="searchButton" disabled>Search</button>
</form>
<ul class="navbar-nav ms-2">
{{ links.navigation_link(endpoint='settings', label = '', icon_html = icons.gear()) }}
</ul>
</div>
</div>
</nav>
@ -66,7 +69,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js"
integrity="sha512-5CYOlHXGh6QpOFA/TeTylKLWfB3ftPsde7AnmhuitiTX4K5SqCLBeKro6sPS8ilsz1Q4NRx3v8Ko2IBiszzdww=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<script src="https://cdn.plot.ly/plotly-3.0.1.min.js" charset="utf-8"></script>
<script
src="https://cdn.datatables.net/v/bs5/jq-3.7.0/moment-2.29.4/dt-2.3.2/b-3.2.3/b-colvis-3.2.3/b-print-3.2.3/cr-2.1.1/cc-1.0.4/r-3.0.4/rg-1.5.1/rr-1.5.0/sc-2.4.3/sr-1.4.1/datatables.min.js"
integrity="sha384-tNYRX2RiDDDRKCJgPF8Pw3rTxC1GUe1pt5qH1SBmwcazrEUj7Ii4C1Tz9wCCRUI4"

73
templates/settings.html Normal file
View file

@ -0,0 +1,73 @@
{% extends "layout.html" %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<form method="POST" action="{{ url_for('main.settings') }}">
{{ breadcrumbs.breadcrumb_header(
title=title,
submit_button=True
) }}
<div class="container">
<label for="brandInput" class="form-label">Brands</label>
<div class="input-group">
<input type="text" class="form-control rounded-bottom-0" id="brandInput" name="brandInput" placeholder="Add a new brand">
<button type="button" class="btn btn-primary rounded-bottom-0" id="addBrandButton" onclick="addBrand();" disabled>
{{ icons.plus(16) }}
</button>
<button type="button" class="btn btn-danger rounded-bottom-0" id="removeBrandButton" onclick="removeSelectedBrands()" disabled>
{{ icons.minus(16) }}
</button>
</div>
<select class="form-select border-top-0 rounded-top-0" id="brandList" size="10" multiple>
{% for brand in brands %}
<option value="{{ brand.id }}">{{ brand.name }}</option>
{% endfor %}
</select>
</div>
</form>
{% endblock %}
{% block script %}
const brandInput = document.querySelector('#brandInput');
const brandList = document.querySelector('#brandList');
const addBrandButton = document.querySelector('#addBrandButton');
const removeBrandButton = document.querySelector('#removeBrandButton');
brandInput.addEventListener('input', () => {
addBrandButton.disabled = brandInput.value.trim() === '';
});
brandList.addEventListener('change', () => {
removeBrandButton.disabled = brandList.selectedOptions.length === 0;
});
function sortOptions() {
const options = Array.from(brandList.options);
options.sort((a, b) => a.text.localeCompare(b.text));
brandList.innerHTML = '';
options.forEach(option => brandList.appendChild(option));
}
let tempIdCounter = -1;
function addBrand() {
const newBrand = brandInput.value.trim();
if (newBrand) {
const option = document.createElement('option');
option.textContent = newBrand;
option.value = tempIdCounter--;
brandList.appendChild(option);
brandInput.value = '';
addBrandButton.disabled = true;
sortOptions();
}
}
function removeSelectedBrands() {
Array.from(brandList.selectedOptions).forEach(option => option.remove());
removeBrandButton.disabled = true;
}
{% endblock %}