Added status table.

This commit is contained in:
Yaro Kasear 2025-10-24 10:44:36 -05:00
parent db287fb8ac
commit f9d950c425
10 changed files with 108 additions and 19 deletions

View file

@ -1,4 +1,4 @@
{# show label unless hidden/custom #}
<!-- FIELD: {{ field_name }} ({{ field_type }}) -->
{% if field_type != 'hidden' and field_label %}
<label for="{{ field_name }}"
{% if label_attrs %}{% for k,v in label_attrs.items() %}

View file

@ -1,5 +1,7 @@
<!-- FORM: {{ model_name|lower }} -->
<form method="POST" id="{{ model_name|lower }}_form">
{% macro render_row(row) %}
<!-- ROW: {{ row['name'] }} -->
{% if row.fields or row.children or row.legend %}
{% if row.legend %}<legend>{{ row.legend }}</legend>{% endif %}
<fieldset

View file

@ -69,6 +69,7 @@ def create_app(config_cls=crudkit.DevConfig) -> Flask:
_models.Inventory,
_models.RoomFunction,
_models.Room,
_models.Status,
_models.User,
_models.WorkLog,
_models.WorkNote,

View file

@ -12,11 +12,12 @@ from .image import Image
from .inventory import Inventory
from .room_function import RoomFunction
from .room import Room
from .status import Status
from .user import User
from .work_log import WorkLog
from .work_note import WorkNote
__all__ = [
"Area", "Brand", "DeviceType", "Image", "Inventory",
"RoomFunction", "Room", "User", "WorkLog", "WorkNote",
"RoomFunction", "Room", "Status", "User", "WorkLog", "WorkNote",
]

View file

@ -17,7 +17,10 @@ class Inventory(Base, CRUDMixin):
name: Mapped[Optional[str]] = mapped_column(Unicode(255), index=True)
serial: Mapped[Optional[str]] = mapped_column(Unicode(255), index=True)
condition: Mapped[str] = mapped_column(Unicode(255))
# condition: Mapped[str] = mapped_column(Unicode(255))
condition: Mapped[Optional['Status']] = relationship('Status', back_populates='inventory')
condition_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey('status.id'), nullable=True, index=True)
model: Mapped[Optional[str]] = mapped_column(Unicode(255))
notes: Mapped[Optional[str]] = mapped_column(Unicode(255))
shared: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=sql.false())

View file

@ -0,0 +1,33 @@
import enum
from typing import List
from sqlalchemy import Boolean, Enum as SAEnum, Unicode
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql import expression as sql
from crudkit.core.base import Base, CRUDMixin
class StatusCategory(str, enum.Enum):
ACTIVE = "Active"
AVAILABLE = "Available"
PENDING = "Pending"
FAULTED = "Faulted"
DECOMMISSIONED = "Decommissioned"
DISPOSED = "Disposed"
ADMINISTRATIVE = "Administrative"
status_type = SAEnum(
StatusCategory,
name="status_category_enum",
validate_strings=True,
)
class Status(Base, CRUDMixin):
__tablename__ = "status"
description: Mapped[str] = mapped_column(Unicode(255), nullable=False, index=True, unique=True)
category: Mapped[StatusCategory] = mapped_column(status_type, nullable=False)
is_deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=sql.false())
inventory: Mapped[List['Inventory']] = relationship('Inventory', back_populates='condition')

View file

@ -9,7 +9,7 @@ from crudkit.core import normalize_payload
bp_entry = Blueprint("entry", __name__)
ENTRY_WHITELIST = ["inventory", "user", "worklog", "room"]
ENTRY_WHITELIST = ["inventory", "user", "worklog", "room", "status"]
def _fields_for_model(model: str):
fields: list[str] = []
@ -53,16 +53,18 @@ def _fields_for_model(model: str):
"attrs": {"class": "form-control"}, "label_attrs": {"class": "form-label"},
"label_spec": "{name} - {room_function.description}"},
{"name": "condition", "label": "Condition", "row": "status", "wrap": {"class": "col"},
"type": "select", "options": [
{"label": "Deployed", "value": "Deployed"},
{"label": "Working", "value": "Working"},
{"label": "Unverified", "value": "Unverified"},
{"label": "Partially Inoperable", "value": "Partially Inoperable"},
{"label": "Inoperable", "value": "Inoperable"},
{"label": "Removed", "value": "Removed"},
{"label": "Disposed", "value": "Disposed"},
],
"label_attrs": {"class": "form-label"}, "attrs": {"class": "form-control"}},
"label_attrs": {"class": "form-label"}},
# {"name": "condition", "label": "Condition", "row": "status", "wrap": {"class": "col"},
# "type": "select", "options": [
# {"label": "Deployed", "value": "Deployed"},
# {"label": "Working", "value": "Working"},
# {"label": "Unverified", "value": "Unverified"},
# {"label": "Partially Inoperable", "value": "Partially Inoperable"},
# {"label": "Inoperable", "value": "Inoperable"},
# {"label": "Removed", "value": "Removed"},
# {"label": "Disposed", "value": "Disposed"},
# ],
# "label_attrs": {"class": "form-label"}, "attrs": {"class": "form-control"}},
{"name": "image", "label": "", "row": "image", "type": "template", "label_spec": "{filename}",
"template": "image_display.html", "attrs": {"class": "img-fluid img-thumbnail h-auto"},
"wrap": {"class": "h-100 w-100"}},
@ -189,6 +191,31 @@ def _fields_for_model(model: str):
{"name": "name", "order": 10, "attrs": {"class": "row"}},
{"name": "details", "order": 20, "attrs": {"class": "row"}},
]
elif model == "status":
fields_spec = [
{"name": "label", "label": "", "type": "display", "attrs": {"class": "display-6 mb-3"},
"row": "label", "wrap": {"class": "col"}, "label_spec": "{description} ({category})"},
{"name": "buttons", "label": "", "row": "label", "type": "template", "template": "entry_buttons.html",
"wrap": {"class": "col-auto text-end me-2"}, "attrs": {"data-model": model}},
{"name": "description", "row": "details", "label": "Description", "attrs": {"class": "form-control"},
"label_attrs": {"class": "form-label"}, "wrap": {"class": "col"}},
{"name": "category", "row": "details", "label": "Category", "attrs": {"class": "form-control"},
"type": "select", "wrap": {"class": "col"}, "label_attrs": {"class": "form-label"}, "options": [
{"label": "Active", "value": "Active"},
{"label": "Available", "value": "Available"},
{"label": "Pending", "value": "Pending"},
{"label": "Faulted", "value": "Faulted"},
{"label": "Decommissioned", "value": "Decommissioned"},
{"label": "Disposed", "value": "Disposed"},
{"label": "Administrative", "value": "Administrative"},
]},
]
layout = [
{"name": "label", "order": 0, "attrs": {"class": "row align-items-center"}},
{"name": "details", "order": 10, "attrs": {"class": "row"}},
]
return (fields, fields_spec, layout)

View file

@ -19,6 +19,8 @@ def init_settings_routes(app):
function_service = crudkit.crud.get_service(function_model)
room_model = crudkit.crud.get_model('room')
room_service = crudkit.crud.get_service(room_model)
status_model = crudkit.crud.get_model('status')
status_service = crudkit.crud.get_service(status_model)
brands = brand_service.list({"sort": "name", "limit": 0})
device_types = device_type_service.list({"sort": "description", "limit": 0})
@ -42,6 +44,16 @@ def init_settings_routes(app):
],
opts={"object_class": 'room'})
return render_template("settings.html", brands=brands, device_types=device_types, areas=areas, functions=functions, rooms=rooms)
statuses = status_service.list({
"sort": "category",
"limit": 0,
"fields": [
"description",
"category",
],
})
statuses = render_table(statuses, opts={"object_class": 'status'})
return render_template("settings.html", brands=brands, device_types=device_types, areas=areas, functions=functions, rooms=rooms, statuses=statuses)
app.register_blueprint(bp_settings)

View file

@ -1,3 +1,4 @@
<!-- TABLE {{ kwargs['object_class'] if kwargs else '(NO MODEL ASSOCIATED)' }} -->
<div class="table-responsive" style="max-height: 80vh;">
<table class="table table-sm table-info table-striped table-hover table-bordered border-tertiary text-nowrap overflow-x-auto mx-auto">
<thead>

View file

@ -24,24 +24,33 @@
<div class="tab-pane fade show active" id="device-tab-pane" tabindex="0">
<div class="row">
<div class="col">
<label for="brand" class="form-label">Brand</label>
<label for="brand" class="form-label">Brands</label>
{{ combobox('brand', 'brand', 'Enter the name of a brand.', brands, 'id', 'name') }}
</div>
<div class="col">
<label for="devicetype" class="form-label">Device Type</label>
<label for="devicetype" class="form-label">Device Types</label>
{{ combobox('devicetype', 'devicetype', 'Enter the description of a device type.', device_types, 'id', 'description') }}
</div>
</div>
<div class="row mt-3">
<div class="col">
<label for="status" class="form-label">
Conditions
<a href="{{ url_for('entry.entry_new', model='status') }}" class="link-success link-underline-opacity-0"><small>[+]</small></a>
</label>
{{ statuses | safe }}
</div>
</div>
</div>
<div class="tab-pane fade" id="location-tab-pane" tabindex="0">
<div class="row">
<div class="col">
<label for="area" class="form-label">Area</label>
<label for="area" class="form-label">Areas</label>
{{ combobox('area', 'area', 'Enter the name of an area.', areas, 'id', 'name') }}
</div>
<div class="col">
<label for="roomfunction" class="form-label">Description</label>
<label for="roomfunction" class="form-label">Descriptions</label>
{{ combobox('roomfunction', 'roomfunction', 'Enter a room description.', functions, 'id', 'description') }}
</div>
</div>