Enhance settings page; add room management functionality with combo box integration and update rendering logic for improved usability

This commit is contained in:
Yaro Kasear 2025-06-18 15:36:34 -05:00
parent ad413c3f1b
commit ec08dd8ef1
4 changed files with 56 additions and 13 deletions

View file

@ -436,4 +436,5 @@ def settings():
types = db.session.query(Item.id, Item.description.label("name")).order_by(Item.description).all() types = db.session.query(Item.id, Item.description.label("name")).order_by(Item.description).all()
sections = db.session.query(Area).order_by(Area.name).all() sections = db.session.query(Area).order_by(Area.name).all()
functions = db.session.query(RoomFunction.id, RoomFunction.description.label("name")).order_by(RoomFunction.description).all() functions = db.session.query(RoomFunction.id, RoomFunction.description.label("name")).order_by(RoomFunction.description).all()
return render_template('settings.html', title="Settings", brands=brands, types=types, sections=sections, functions=functions) rooms = eager_load_room_relationships(db.session.query(Room).order_by(Room.name)).all()
return render_template('settings.html', title="Settings", brands=brands, types=types, sections=sections, functions=functions, rooms=rooms)

View file

@ -1,12 +1,13 @@
const ComboBoxWidget = (() => { const ComboBoxWidget = (() => {
let tempIdCounter = -1; let tempIdCounter = -1;
function initComboBox(ns) { function initComboBox(ns, config = {}) {
const input = document.querySelector(`#${ns}-input`); const input = document.querySelector(`#${ns}-input`);
const list = document.querySelector(`#${ns}-list`); const list = document.querySelector(`#${ns}-list`);
const addBtn = document.querySelector(`#${ns}-add`); const addBtn = document.querySelector(`#${ns}-add`);
const removeBtn = document.querySelector(`#${ns}-remove`); const removeBtn = document.querySelector(`#${ns}-remove`);
let currentlyEditing = null; let currentlyEditing = null;
let tempIdCounter = -1;
if (!input || !list || !addBtn || !removeBtn) { if (!input || !list || !addBtn || !removeBtn) {
console.warn(`ComboBoxWidget: Missing elements for namespace '${ns}'`); console.warn(`ComboBoxWidget: Missing elements for namespace '${ns}'`);
@ -19,6 +20,7 @@ const ComboBoxWidget = (() => {
input.addEventListener('input', () => { input.addEventListener('input', () => {
addBtn.disabled = input.value.trim() === ''; addBtn.disabled = input.value.trim() === '';
updateAddButtonIcon();
}); });
list.addEventListener('change', () => { list.addEventListener('change', () => {
@ -26,10 +28,9 @@ const ComboBoxWidget = (() => {
removeBtn.disabled = selected.length === 0; removeBtn.disabled = selected.length === 0;
if (selected.length === 1) { if (selected.length === 1) {
// Load the text into input for editing
input.value = selected[0].textContent; input.value = selected[0].textContent;
addBtn.disabled = input.value.trim() === '';
currentlyEditing = selected[0]; currentlyEditing = selected[0];
addBtn.disabled = input.value.trim() === '';
} else { } else {
input.value = ''; input.value = '';
currentlyEditing = null; currentlyEditing = null;
@ -44,28 +45,55 @@ const ComboBoxWidget = (() => {
if (!newItem) return; if (!newItem) return;
if (currentlyEditing) { if (currentlyEditing) {
currentlyEditing.textContent = newItem; if (config.onEdit) {
config.onEdit(currentlyEditing, newItem);
} else {
currentlyEditing.textContent = newItem;
}
currentlyEditing = null; currentlyEditing = null;
} else { } else {
const option = document.createElement('option'); const option = document.createElement('option');
option.textContent = newItem; option.textContent = newItem;
option.value = tempIdCounter--; option.value = tempIdCounter--;
if (config.onAdd) {
config.onAdd(option);
}
list.appendChild(option); list.appendChild(option);
} }
input.value = ''; input.value = '';
addBtn.disabled = true; addBtn.disabled = true;
removeBtn.disabled = true; removeBtn.disabled = true;
sortOptions(list); updateAddButtonIcon();
if (config.sort !== false) {
sortOptions(list);
}
}); });
removeBtn.addEventListener('click', () => { removeBtn.addEventListener('click', () => {
Array.from(list.selectedOptions).forEach(opt => opt.remove()); Array.from(list.selectedOptions).forEach(option => {
if (config.onRemove) {
config.onRemove(option);
}
option.remove();
});
currentlyEditing = null; currentlyEditing = null;
removeBtn.disabled = true;
input.value = ''; input.value = '';
addBtn.disabled = true; addBtn.disabled = true;
removeBtn.disabled = true;
updateAddButtonIcon();
}); });
function sortOptions(selectElement) {
const sorted = Array.from(selectElement.options)
.sort((a, b) => a.text.localeCompare(b.text));
selectElement.innerHTML = '';
sorted.forEach(option => selectElement.appendChild(option));
}
} }
function sortOptions(selectElement) { function sortOptions(selectElement) {

View file

@ -1,6 +1,6 @@
{% import "fragments/_icon_fragment.html" as icons %} {% import "fragments/_icon_fragment.html" as icons %}
{% macro render_combobox(id, options, label=none, placeholder=none) %} {% macro render_combobox(id, options, label=none, placeholder=none, onAdd=none, onRemove=none, onEdit=none) %}
{% if label %} {% if label %}
<label for="{{ id }}-input" class="form-label">{{ label }}</label> <label for="{{ id }}-input" class="form-label">{{ label }}</label>
{% endif %} {% endif %}
@ -23,7 +23,11 @@
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
ComboBoxWidget.initComboBox("{{ id }}"); ComboBoxWidget.initComboBox("{{ id }}", {
{% if onAdd %}onAdd: {{ onAdd }},{% endif %}
{% if onRemove %}onRemove: {{ onRemove }},{% endif %}
{% if onEdit %}onEdit: {{ onEdit }}{% endif %}
});
}); });
</script> </script>
{% endmacro %} {% endmacro %}

View file

@ -57,6 +57,16 @@
) }} ) }}
</div> </div>
</div> </div>
<div class="row">
<div class="col">
{{ combos.render_combobox(
id='room',
options=rooms,
label='Rooms',
placeholder='Add a new room'
) }}
</div>
</div>
</div> </div>
</div> </div>
</div> </div>