Compare commits
2 commits
d12023ecd1
...
69a4a19587
Author | SHA1 | Date | |
---|---|---|---|
![]() |
69a4a19587 | ||
![]() |
462c077681 |
13 changed files with 139 additions and 88 deletions
|
@ -74,10 +74,10 @@ class Inventory(db.Model, ImageAttachable):
|
||||||
parts.append(f"notes={repr(self.notes)}")
|
parts.append(f"notes={repr(self.notes)}")
|
||||||
|
|
||||||
if self.owner:
|
if self.owner:
|
||||||
parts.append(f"owner={repr(self.owner.full_name)}")
|
parts.append(f"owner={repr(self.owner.identifier)}")
|
||||||
|
|
||||||
if self.location:
|
if self.location:
|
||||||
parts.append(f"location={repr(self.location.full_name)}")
|
parts.append(f"location={repr(self.location.identifier)}")
|
||||||
|
|
||||||
return f"<Inventory({', '.join(parts)})>"
|
return f"<Inventory({', '.join(parts)})>"
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ class Room(ValidatableMixin, db.Model):
|
||||||
return f"<Room(id={self.id}, room={repr(self.name)}, area_id={self.area_id}, function_id={self.function_id})>"
|
return f"<Room(id={self.id}, room={repr(self.name)}, area_id={self.area_id}, function_id={self.function_id})>"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_name(self):
|
def identifier(self):
|
||||||
name = self.name or ""
|
name = self.name or ""
|
||||||
func = self.room_function.description if self.room_function else ""
|
func = self.room_function.description if self.room_function else ""
|
||||||
return f"{name} - {func}".strip(" -")
|
return f"{name} - {func}".strip(" -")
|
||||||
|
|
|
@ -31,7 +31,7 @@ class User(db.Model, ImageAttachable):
|
||||||
image: Mapped[Optional['Image']] = relationship('Image', back_populates='user', passive_deletes=True)
|
image: Mapped[Optional['Image']] = relationship('Image', back_populates='user', passive_deletes=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_name(self) -> str:
|
def identifier(self) -> str:
|
||||||
return f"{self.first_name or ''} {self.last_name or ''}".strip()
|
return f"{self.first_name or ''} {self.last_name or ''}".strip()
|
||||||
|
|
||||||
def __init__(self, first_name: Optional[str] = None, last_name: Optional[str] = None,
|
def __init__(self, first_name: Optional[str] = None, last_name: Optional[str] = None,
|
||||||
|
|
|
@ -20,8 +20,8 @@ inventory_headers = {
|
||||||
"Model": lambda i: {"text": i.model},
|
"Model": lambda i: {"text": i.model},
|
||||||
"Item Type": lambda i: {"text": i.item.description} if i.item else {"text": None},
|
"Item Type": lambda i: {"text": i.item.description} if i.item else {"text": None},
|
||||||
"Shared?": lambda i: {"text": i.shared, "type": "bool", "html": checked_box if i.shared else unchecked_box},
|
"Shared?": lambda i: {"text": i.shared, "type": "bool", "html": checked_box if i.shared else unchecked_box},
|
||||||
"Owner": lambda i: {"text": i.owner.full_name, "url": url_for("main.user", id=i.owner.id)} if i.owner else {"text": None},
|
"Owner": lambda i: {"text": i.owner.identifier, "url": url_for("main.user", id=i.owner.id)} if i.owner else {"text": None},
|
||||||
"Location": lambda i: {"text": i.location.full_name} if i.location else {"Text": None},
|
"Location": lambda i: {"text": i.location.identifier} if i.location else {"Text": None},
|
||||||
"Condition": lambda i: {"text": i.condition}
|
"Condition": lambda i: {"text": i.condition}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,14 +52,14 @@ FILTER_MAP = {
|
||||||
user_headers = {
|
user_headers = {
|
||||||
"Last Name": lambda i: {"text": i.last_name},
|
"Last Name": lambda i: {"text": i.last_name},
|
||||||
"First Name": lambda i: {"text": i.first_name},
|
"First Name": lambda i: {"text": i.first_name},
|
||||||
"Supervisor": lambda i: {"text": i.supervisor.full_name, "url": url_for("main.user", id=i.supervisor.id)} if i.supervisor else {"text": None},
|
"Supervisor": lambda i: {"text": i.supervisor.identifier, "url": url_for("main.user", id=i.supervisor.id)} if i.supervisor else {"text": None},
|
||||||
"Location": lambda i: {"text": i.location.full_name} if i.location else {"text": None},
|
"Location": lambda i: {"text": i.location.identifier} if i.location else {"text": None},
|
||||||
"Staff?": lambda i: {"text": i.staff, "type": "bool", "html": checked_box if i.staff else unchecked_box},
|
"Staff?": lambda i: {"text": i.staff, "type": "bool", "html": checked_box if i.staff else unchecked_box},
|
||||||
"Active?": lambda i: {"text": i.active, "type": "bool", "html": checked_box if i.active else unchecked_box}
|
"Active?": lambda i: {"text": i.active, "type": "bool", "html": checked_box if i.active else unchecked_box}
|
||||||
}
|
}
|
||||||
|
|
||||||
worklog_headers = {
|
worklog_headers = {
|
||||||
"Contact": lambda i: {"text": i.contact.full_name, "url": url_for("main.user", id=i.contact.id)} if i.contact else {"Text": None},
|
"Contact": lambda i: {"text": i.contact.identifier, "url": url_for("main.user", id=i.contact.id)} if i.contact else {"Text": None},
|
||||||
"Work Item": lambda i: {"text": i.work_item.identifier, "url": url_for('main.inventory_item',id=i.work_item.id)} if i.work_item else {"text": None},
|
"Work Item": lambda i: {"text": i.work_item.identifier, "url": url_for('main.inventory_item',id=i.work_item.id)} if i.work_item else {"text": None},
|
||||||
"Start Time": lambda i: {"text": i.start_time.strftime("%Y-%m-%d")},
|
"Start Time": lambda i: {"text": i.start_time.strftime("%Y-%m-%d")},
|
||||||
"End Time": lambda i: {"text": i.end_time.strftime("%Y-%m-%d")} if i.end_time else {"text": None},
|
"End Time": lambda i: {"text": i.end_time.strftime("%Y-%m-%d")} if i.end_time else {"text": None},
|
||||||
|
|
|
@ -61,10 +61,10 @@ def index():
|
||||||
users = set([log.contact for log in worklog_query if log.contact])
|
users = set([log.contact for log in worklog_query if log.contact])
|
||||||
work_summary = {}
|
work_summary = {}
|
||||||
|
|
||||||
for user in sorted(users, key=lambda u: u.full_name):
|
for user in sorted(users, key=lambda u: u.identifier):
|
||||||
work_summary[user.full_name] = {}
|
work_summary[user.identifier] = {}
|
||||||
work_summary[user.full_name]['active_count'] = len([log for log in worklog_query if log.contact == user and not log.complete])
|
work_summary[user.identifier]['active_count'] = len([log for log in worklog_query if log.contact == user and not log.complete])
|
||||||
work_summary[user.full_name]['complete_count'] = len([log for log in worklog_query if log.contact == user and log.complete])
|
work_summary[user.identifier]['complete_count'] = len([log for log in worklog_query if log.contact == user and log.complete])
|
||||||
|
|
||||||
datasets['work_summary'] = [{
|
datasets['work_summary'] = [{
|
||||||
'type': 'bar',
|
'type': 'bar',
|
||||||
|
|
|
@ -31,11 +31,11 @@ def list_inventory():
|
||||||
if filter_by == 'user':
|
if filter_by == 'user':
|
||||||
if not (user := db.session.query(User).filter(User.id == id).first()):
|
if not (user := db.session.query(User).filter(User.id == id).first()):
|
||||||
return "Invalid User ID", 400
|
return "Invalid User ID", 400
|
||||||
filter_name = user.full_name
|
filter_name = user.identifier
|
||||||
elif filter_by == 'location':
|
elif filter_by == 'location':
|
||||||
if not (room := db.session.query(Room).filter(Room.id == id).first()):
|
if not (room := db.session.query(Room).filter(Room.id == id).first()):
|
||||||
return "Invalid Location ID", 400
|
return "Invalid Location ID", 400
|
||||||
filter_name = room.full_name
|
filter_name = room.identifier
|
||||||
else:
|
else:
|
||||||
if not (item := db.session.query(Item).filter(Item.id == id).first()):
|
if not (item := db.session.query(Item).filter(Item.id == id).first()):
|
||||||
return "Invalid Type ID", 400
|
return "Invalid Type ID", 400
|
||||||
|
@ -220,9 +220,9 @@ def get_inventory_csv():
|
||||||
case "brand":
|
case "brand":
|
||||||
return item.brand.name
|
return item.brand.name
|
||||||
case "location":
|
case "location":
|
||||||
return item.location.full_name
|
return item.location.identifier
|
||||||
case "owner":
|
case "owner":
|
||||||
return item.owner.full_name
|
return item.owner.identifier
|
||||||
case "type":
|
case "type":
|
||||||
return item.item.description
|
return item.item.description
|
||||||
case _:
|
case _:
|
||||||
|
|
|
@ -50,7 +50,7 @@ def user(id):
|
||||||
filtered_worklog_headers = {k: v for k, v in worklog_headers.items() if k not in ['Contact', 'Follow Up?', 'Quick Analysis?']}
|
filtered_worklog_headers = {k: v for k, v in worklog_headers.items() if k not in ['Contact', 'Follow Up?', 'Quick Analysis?']}
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
title = f"User Record - {user.full_name}" if user.active else f"User Record - {user.full_name} (Inactive)"
|
title = f"User Record - {user.identifier}" if user.active else f"User Record - {user.identifier} (Inactive)"
|
||||||
else:
|
else:
|
||||||
title = f"User Record - User Not Found"
|
title = f"User Record - User Not Found"
|
||||||
return render_template(
|
return render_template(
|
||||||
|
@ -133,9 +133,9 @@ def get_user_csv():
|
||||||
try:
|
try:
|
||||||
match col:
|
match col:
|
||||||
case "location":
|
case "location":
|
||||||
return user.location.full_name
|
return user.location.identifier
|
||||||
case "supervisor":
|
case "supervisor":
|
||||||
return user.supervisor.full_name
|
return user.supervisor.identifier
|
||||||
case _:
|
case _:
|
||||||
return getattr(user, col, "")
|
return getattr(user, col, "")
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
|
@ -153,7 +153,7 @@ def get_worklog_csv():
|
||||||
try:
|
try:
|
||||||
match col:
|
match col:
|
||||||
case "contact":
|
case "contact":
|
||||||
return log.contact.full_name
|
return log.contact.identifier
|
||||||
case "work_item":
|
case "work_item":
|
||||||
return log.work_item.identifier
|
return log.work_item.identifier
|
||||||
case "latest_update":
|
case "latest_update":
|
||||||
|
|
57
inventory/templates/fragments/_dropdown_fragment.html
Normal file
57
inventory/templates/fragments/_dropdown_fragment.html
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{% macro render_dropdown(id, list, label, current_item = None, entry_link = None) %}
|
||||||
|
<label for="{{ id }}" class="form-label">
|
||||||
|
{{ label }}
|
||||||
|
{% if entry_link %}
|
||||||
|
{{ links.entry_link(entry_link, current_item.id) }}
|
||||||
|
{% endif %}
|
||||||
|
</label>
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-outline-dark dropdown-toggle w-100" type="button" data-bs-toggle="dropdown"
|
||||||
|
data-inv-value="{{ current_item.id if current_item else '' }}" id="{{ id }}Button">
|
||||||
|
{{ current_item.identifier if current_item else '-' }}
|
||||||
|
</button>
|
||||||
|
<input type="hidden" name="{{ id }}" id="{{ id }}" value="{{ current_item.id if current_item else '' }}">
|
||||||
|
<ul class="dropdown-menu w-100" id="menu{{ id }}">
|
||||||
|
<input type="text" class="form-control" id="search{{ id }}" placeholder="Search...">
|
||||||
|
{% for item in list %}
|
||||||
|
<li><a class="dropdown-item" data-inv-value="{{ item.id }}" onclick="{{ id }}SetButton({{ item.id }}, '{{ item.identifier }}')">{{ item.identifier }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function {{ id }}SetButton(id, identifier) {
|
||||||
|
const button = document.getElementById("{{ id }}Button");
|
||||||
|
const input = document.getElementById("{{ id }}");
|
||||||
|
button.dataset.invValue = id;
|
||||||
|
button.textContent = identifier;
|
||||||
|
input.value = id;
|
||||||
|
console.log("Selected {{ id }} ID:", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const {{ id }}Dropdown = document.getElementById("menu{{ id }}");
|
||||||
|
const {{ id }}Input = document.getElementById("{{ id }}");
|
||||||
|
|
||||||
|
{{ id }}Dropdown.addEventListener("click", (e) => {
|
||||||
|
if (e.target.tagName === "A") {
|
||||||
|
{{ id }}Input.value = e.target.dataset.invValue;
|
||||||
|
console.log("Selected {{ id }} ID:", {{ id }}Input.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const {{ id }}SearchInput = document.getElementById("search{{ id }}");
|
||||||
|
console.log({{ id }}SearchInput);
|
||||||
|
{{ id }}SearchInput.addEventListener("input", () => {
|
||||||
|
const filter = {{ id }}SearchInput.value.toLowerCase();
|
||||||
|
const items = {{ id }}Dropdown.querySelectorAll("a.dropdown-item");
|
||||||
|
items.forEach(item => {
|
||||||
|
if (item.textContent.toLowerCase().includes(filter)) {
|
||||||
|
item.style.display = "";
|
||||||
|
} else {
|
||||||
|
item.style.display = "none";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endmacro %}
|
|
@ -209,7 +209,7 @@
|
||||||
<option>-</option>
|
<option>-</option>
|
||||||
{% for user in users %}
|
{% for user in users %}
|
||||||
<option value="{{ user.id }}" {% if user.id==item.owner_id %} selected{% endif %}>{{
|
<option value="{{ user.id }}" {% if user.id==item.owner_id %} selected{% endif %}>{{
|
||||||
user.full_name
|
user.identifier
|
||||||
}}</option>
|
}}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
@ -221,7 +221,7 @@
|
||||||
<option>-</option>
|
<option>-</option>
|
||||||
{% for room in rooms %}
|
{% for room in rooms %}
|
||||||
<option value="{{ room.id }}" {% if room.id==item.location_id %} selected{% endif %}>{{
|
<option value="{{ room.id }}" {% if room.id==item.location_id %} selected{% endif %}>{{
|
||||||
room.full_name }}</option>
|
room.identifier }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{% import "fragments/_button_fragment.html" as buttons %}
|
{% import "fragments/_button_fragment.html" as buttons %}
|
||||||
{% import "fragments/_breadcrumb_fragment.html" as breadcrumbs %}
|
{% import "fragments/_breadcrumb_fragment.html" as breadcrumbs %}
|
||||||
{% import "fragments/_combobox_fragment.html" as combos %}
|
{% import "fragments/_combobox_fragment.html" as combos %}
|
||||||
|
{% import "fragments/_dropdown_fragment.html" as dropdowns %}
|
||||||
{% import "fragments/_editor_fragment.html" as editor %}
|
{% import "fragments/_editor_fragment.html" as editor %}
|
||||||
{% import "fragments/_icon_fragment.html" as icons %}
|
{% import "fragments/_icon_fragment.html" as icons %}
|
||||||
{% import "fragments/_image_fragment.html" as images %}
|
{% import "fragments/_image_fragment.html" as images %}
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
<option>-</option>
|
<option>-</option>
|
||||||
{% for supervisor in users %}
|
{% for supervisor in users %}
|
||||||
<option value="{{ supervisor.id }}"{% if supervisor.id==user.supervisor_id %} selected{% endif %}>
|
<option value="{{ supervisor.id }}"{% if supervisor.id==user.supervisor_id %} selected{% endif %}>
|
||||||
{{ supervisor.full_name }}</option>
|
{{ supervisor.identifier }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -119,7 +119,7 @@
|
||||||
<option>-</option>
|
<option>-</option>
|
||||||
{% for location in rooms %}
|
{% for location in rooms %}
|
||||||
<option value="{{ location.id }}"{% if location.id==user.location_id %} selected{% endif %}>{{
|
<option value="{{ location.id }}"{% if location.id==user.location_id %} selected{% endif %}>{{
|
||||||
location.full_name }}</option>
|
location.identifier }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -142,7 +142,7 @@
|
||||||
{% set inventory_title %}
|
{% set inventory_title %}
|
||||||
Assets
|
Assets
|
||||||
{{ links.export_link(
|
{{ links.export_link(
|
||||||
(user.full_name | lower | replace(' ', '_')) + '_user_inventory',
|
(user.identifier | lower | replace(' ', '_')) + '_user_inventory',
|
||||||
'inventory',
|
'inventory',
|
||||||
{'ids': id_list}
|
{'ids': id_list}
|
||||||
) }}
|
) }}
|
||||||
|
@ -157,7 +157,7 @@
|
||||||
{% set worklog_title %}
|
{% set worklog_title %}
|
||||||
Work Done
|
Work Done
|
||||||
{{ links.export_link(
|
{{ links.export_link(
|
||||||
(user.full_name | lower | replace(' ', '_')) + '_user_worklog',
|
(user.identifier | lower | replace(' ', '_')) + '_user_worklog',
|
||||||
'worklog',
|
'worklog',
|
||||||
{'ids': id_list}
|
{'ids': id_list}
|
||||||
) }}
|
) }}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
analysis: document.querySelector("input[name='analysis']").checked,
|
analysis: document.querySelector("input[name='analysis']").checked,
|
||||||
followup: document.querySelector("input[name='followup']").checked,
|
followup: document.querySelector("input[name='followup']").checked,
|
||||||
contact_id: parseInt(document.querySelector("select[name='contact']").value) || null,
|
contact_id: parseInt(document.querySelector("select[name='contact']").value) || null,
|
||||||
work_item_id: parseInt(document.querySelector("select[name='item']").value) || null,
|
work_item_id: parseInt(document.querySelector("input[name='item']").value) || null,
|
||||||
updates: updates
|
updates: updates
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -163,25 +163,18 @@
|
||||||
<option value="">-</option>
|
<option value="">-</option>
|
||||||
{% for contact in users %}
|
{% for contact in users %}
|
||||||
<option value="{{ contact.id }}" {% if contact.id==log.contact_id %} selected{% endif %}>{{
|
<option value="{{ contact.id }}" {% if contact.id==log.contact_id %} selected{% endif %}>{{
|
||||||
contact.full_name }}
|
contact.identifier }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label for="item" class="form-label">
|
{{ dropdowns.render_dropdown(
|
||||||
Work Item
|
id='item',
|
||||||
{% if log.work_item_id %}
|
list=items,
|
||||||
{{ links.entry_link('inventory_item', log.work_item_id) }}
|
label='Work Item',
|
||||||
{% endif %}
|
current_item=log.work_item
|
||||||
</label>
|
) }}
|
||||||
<select id="item" name="item" class="form-select"{% if log.complete %} disabled{% endif %}>
|
|
||||||
<option value="">-</option>
|
|
||||||
{% for item in items %}
|
|
||||||
<option value="{{ item.id }}" {% if item.id==log.work_item_id %} selected{% endif %}>{{ item.identifier
|
|
||||||
}}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue