From 1ea6cd9588090a9c8d17c9bfb7bd72d617cbbd0c Mon Sep 17 00:00:00 2001 From: Yaro Kasear Date: Thu, 14 Aug 2025 10:33:28 -0500 Subject: [PATCH] Cleanup. --- inventory/static/js/combobox.js | 139 +-------------- .../fragments/_combobox_fragment.html | 41 +---- inventory/templates/playground.html | 2 +- inventory/templates/settings.html | 165 +----------------- 4 files changed, 10 insertions(+), 337 deletions(-) diff --git a/inventory/static/js/combobox.js b/inventory/static/js/combobox.js index 03f4258..558b037 100644 --- a/inventory/static/js/combobox.js +++ b/inventory/static/js/combobox.js @@ -1,133 +1,3 @@ -const ComboBoxWidget = (() => { - let tempIdCounter = 1; - - function createTempId(prefix = "temp") { - return `${prefix}-${tempIdCounter++}`; - } - - function createOption(text, value = null) { - const option = document.createElement('option'); - option.textContent = text; - option.value = value ?? createTempId(); - return option; - } - - 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 initComboBox(ns, config = {}) { - const input = document.querySelector(`#${ns}-input`); - const list = document.querySelector(`#${ns}-list`); - const addBtn = document.querySelector(`#${ns}-add`); - const removeBtn = document.querySelector(`#${ns}-remove`); - let currentlyEditing = null; - - if (!input || !list || !addBtn || !removeBtn) { - console.warn(`ComboBoxWidget: Missing elements for namespace '${ns}'`); - return; - } - - function updateAddButtonIcon() { - const iconEl = addBtn.querySelector('.icon-state'); - - const iconClass = currentlyEditing ? 'bi-pencil' : 'bi-plus-lg'; - iconEl.classList.forEach(cls => { - if (cls.startsWith('bi-') && cls !== 'icon-state') { - iconEl.classList.remove(cls); - } - }); - iconEl.classList.add(iconClass); - } - - input.addEventListener('input', () => { - addBtn.disabled = input.value.trim() === ''; - updateAddButtonIcon(); - }); - - list.addEventListener('change', () => { - const selected = list.selectedOptions; - removeBtn.disabled = selected.length === 0; - - if (selected.length === 1) { - input.value = selected[0].textContent.trim(); - currentlyEditing = selected[0]; - addBtn.disabled = input.value.trim() === ''; - } else { - input.value = ''; - currentlyEditing = null; - addBtn.disabled = true; - } - - updateAddButtonIcon(); - }); - - addBtn.addEventListener('click', () => { - const newItem = input.value.trim(); - if (!newItem) return; - - if (currentlyEditing) { - if (config.onEdit) { - config.onEdit(currentlyEditing, newItem); - } else { - currentlyEditing.textContent = newItem; - } - currentlyEditing = null; - } else { - if (config.onAdd) { - config.onAdd(newItem, list, createOption); - return; // Skip the default logic! - } - - const exists = Array.from(list.options).some(opt => opt.textContent === newItem); - if (exists) { - alert(`"${newItem}" already exists.`); - return; - } - - const option = createOption(newItem); - list.appendChild(option); - - const key = config.stateArray ?? `${ns}s`; // fallback to pluralization - - if (config.sort !== false) { - sortOptions(list); - } - } - - input.value = ''; - addBtn.disabled = true; - removeBtn.disabled = true; - updateAddButtonIcon(); - }); - - removeBtn.addEventListener('click', () => { - Array.from(list.selectedOptions).forEach(option => { - if (config.onRemove) { - config.onRemove(option); - } - option.remove(); - }); - - currentlyEditing = null; - input.value = ''; - addBtn.disabled = true; - removeBtn.disabled = true; - updateAddButtonIcon(); - }); - } - - return { - initComboBox, - createOption, - sortOptions, - createTempId - }; -})(); - function ComboBox(cfg) { return { id: cfg.id, @@ -139,16 +9,13 @@ function ComboBox(cfg) { query: '', isEditing: false, editingOption: null, - - // NEW: keep selection reactive selectedIds: [], - // Button disable uses reactive data now get hasSelection() { return this.selectedIds.length > 0 }, onListChange() { const sel = Array.from(this.$refs.list.selectedOptions); - this.selectedIds = sel.map(o => o.value); // <-- reactive update + this.selectedIds = sel.map(o => o.value); if (sel.length === 1) { this.query = sel[0].textContent.trim(); @@ -173,13 +40,11 @@ function ComboBox(cfg) { const data = await this._post(this.createUrl, { name }, true); const id = (data && data.id) ? data.id : ('temp-' + Math.random().toString(36).slice(2)); - // add option optimistically const opt = document.createElement('option'); opt.value = id; opt.textContent = data?.name || name; this.$refs.list.appendChild(opt); this._sortOptions(); - // ✅ NEW: tell the world we created something this.$dispatch('combobox:item-created', { id, name: data?.name || name }); } @@ -189,7 +54,7 @@ function ComboBox(cfg) { }, async removeSelected() { - const ids = [...this.selectedIds]; // <-- capture IDs before DOM changes + const ids = [...this.selectedIds]; if (!ids.length) return; if (!confirm(`Delete ${ids.length} item(s)?`)) return; diff --git a/inventory/templates/fragments/_combobox_fragment.html b/inventory/templates/fragments/_combobox_fragment.html index 3cd7720..798bcec 100644 --- a/inventory/templates/fragments/_combobox_fragment.html +++ b/inventory/templates/fragments/_combobox_fragment.html @@ -1,45 +1,6 @@ {% import "fragments/_icon_fragment.html" as icons %} -{% macro render_combobox(id, options, label=none, placeholder=none, onAdd=none, onRemove=none, onEdit=none, -data_attributes=none) %} - - -{% if label %} - -{% endif %} -
-
- - - -
- -
- - -{% endmacro %} - -{% macro dynamic_combobox( +{% macro render_combobox( id, options, label = none, placeholder = none, data_attributes = none, create_url = none, edit_url = none, delete_url = none, refresh_url = none ) %} diff --git a/inventory/templates/playground.html b/inventory/templates/playground.html index 3b02008..a5c09d2 100644 --- a/inventory/templates/playground.html +++ b/inventory/templates/playground.html @@ -1,7 +1,7 @@ {% extends 'layout.html' %} {% block content %} - {{ combos.dynamic_combobox( + {{ combos.render_combobox( id='play', label='Breakfast!', create_url=url_for('ui.create_item', model_name='brand'), diff --git a/inventory/templates/settings.html b/inventory/templates/settings.html index 35ee1b4..f6717dc 100644 --- a/inventory/templates/settings.html +++ b/inventory/templates/settings.html @@ -3,108 +3,9 @@ {% block title %}{{ title }}{% endblock %} {% block precontent %} - {% set saveLogic %} - function isSerializable(obj) { - try { - JSON.stringify(obj); - return true; - } catch { - return false; - } - } - - function buildFormState() { - function extractOptions(id) { - const select = document.getElementById(`${id}-list`); - return Array.from(select.options).map(opt => { - const name = opt.textContent.trim(); - const rawId = opt.value?.trim(); - return { - name, - ...(rawId ? { id: /^\d+$/.test(rawId) ? parseInt(rawId, 10) : rawId } : {}) - }; - }); - } - - function sanitizeFk(val) { - if (val && val !== "null" && val !== "" && val !== "None") { - return /^\d+$/.test(val) ? parseInt(val, 10) : val; - } - return null; - } - - const roomOptions = Array.from(document.getElementById('room-list').options); - - const rooms = roomOptions.map(opt => { - const id = opt.value?.trim(); - const name = opt.textContent.trim(); - const sectionId = sanitizeFk(opt.dataset.sectionId); - const functionId = sanitizeFk(opt.dataset.functionId); - - const result = { - name, - ...(id ? { id } : {}), - area_id: sectionId, - function_id: functionId - }; - - return result; - }); - - return { - brands: extractOptions("brand"), - types: extractOptions("type"), - sections: extractOptions("section"), - functions: extractOptions("function"), - rooms - }; - } - - event.preventDefault(); - - const state = buildFormState(); - - if (!isSerializable(state)) { - console.warn("🚨 Payload is not serializable:", state); - alert("Invalid payload — check console."); - return; - } - - try { - const response = await fetch('/api/settings', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(state) - }); - - const contentType = response.headers.get("content-type"); - - if (!response.ok) { - if (contentType && contentType.includes("application/json")) { - const data = await response.json(); - throw new Error(data.errors?.join("\n") || "Unknown error"); - } else { - const text = await response.text(); - throw new Error("Unexpected response:\n" + text.slice(0, 200)); - } - } - - const data = await response.json(); - Toast.renderToast({ message: 'Settings updated successfully.', type: 'success' }); - - } catch (err) { - Toast.renderToast({ message: `Failed to update settings, ${err}`, type: 'danger' }); - } - {% endset %} {{ toolbars.render_toolbar( id='settings', - left=breadcrumb_macro.render_breadcrumb(breadcrumbs=breadcrumbs), - right=buttons.render_button( - id='save', - icon='floppy', - logic=saveLogic, - style='outline-primary' - ) + left=breadcrumb_macro.render_breadcrumb(breadcrumbs=breadcrumbs) ) }} {% endblock %} @@ -128,7 +29,7 @@
- {{ combos.dynamic_combobox( + {{ combos.render_combobox( id='brand', label='Brands', placeholder='Add a new brand', @@ -139,7 +40,7 @@ ) }}
- {{ combos.dynamic_combobox( + {{ combos.render_combobox( id='type', label='Inventory Types', placeholder='Add a new type', @@ -154,7 +55,7 @@
- {{ combos.dynamic_combobox( + {{ combos.render_combobox( id='section', label='Sections', placeholder='Add a new section', @@ -165,7 +66,7 @@ ) }}
- {{ combos.dynamic_combobox( + {{ combos.render_combobox( id='function', label='Functions', placeholder='Add a new function', @@ -177,29 +78,8 @@
- {% set room_editor %} - const roomEditorModal = new bootstrap.Modal(document.getElementById('roomEditor')); - const input = document.getElementById('room-input'); - const name = input.value.trim(); - const existingOption = Array.from(document.getElementById('room-list').options) - .find(opt => opt.textContent.trim() === name); - - const event = new CustomEvent('roomEditor:prepare', { - detail: { - id: existingOption?.value ?? '', - name: name, - sectionId: existingOption?.dataset.sectionId ?? '', - functionId: existingOption?.dataset.functionId ?? '' - } - }); - - document.getElementById('roomEditor').dispatchEvent(event); - roomEditorModal.show(); - - document.getElementById('room-input').value = ''; - {% endset %}
- {{ combos.dynamic_combobox( + {{ combos.render_combobox( id='room', label='Rooms', placeholder='Add a new room', @@ -268,39 +148,6 @@ {% set editorSaveLogic %} - {# - const modal = document.getElementById('roomEditor'); - const name = document.getElementById('roomName').value.trim(); - const sectionVal = document.getElementById('roomSection').value; - const funcVal = document.getElementById('roomFunction').value; - let idRaw = document.getElementById('roomId').value; - - if (!name) { - alert('Please enter a room name.'); - return; - } - - const roomList = document.getElementById('room-list'); - let existingOption = Array.from(roomList.options).find(opt => opt.value === idRaw); - - if (!idRaw) { - idRaw = ComboBoxWidget.createTempId("room"); - } - - if (!existingOption) { - existingOption = ComboBoxWidget.createOption(name, idRaw); - roomList.appendChild(existingOption); - } - - existingOption.textContent = name; - existingOption.value = idRaw; - existingOption.dataset.sectionId = sectionVal || ""; - existingOption.dataset.functionId = funcVal || ""; - - ComboBoxWidget.sortOptions(roomList); - - bootstrap.Modal.getInstance(modal).hide(); - #} const modalEl = document.getElementById('roomEditor'); const idRaw = document.getElementById('roomId').value; const name = document.getElementById('roomName').value.trim();