From 4dc5bdf8cf3e6cd6e1e961cacfc6a84d8b60a6d9 Mon Sep 17 00:00:00 2001 From: "Valentin V. Bartenev" Date: Mon, 11 May 2026 04:00:41 +0300 Subject: [PATCH] Web: rework MQTT IATA field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the hardcoded 24-city + pair. The datalist is populated at runtime from the same conf.json endpoint used for radio presets, so the city list stays in sync with the API without requiring firmware updates. The allows the user to type any IATA code directly, including codes not present in the preset list. onfocus="this.select()" selects all text on focus so the user can immediately type to replace the current value without manually clearing it first. Disable the Save button in HTML and enable it only when the field is non-empty. The event handler updates the button state as the user types so Save is disabled immediately when the field is cleared. refreshMqttIataWarning() also updates the button state after a Refresh or Save, ensuring the button is re-enabled when the device value is loaded. The warning banner and inline warning are not updated while the user is typing — the event handler does not call refreshMqttIataWarning(). The warning only reflects the device's saved state, updated on Refresh and after a successful Save. Switch the event listener from "change" to "input" so the Save button responds to every keystroke rather than only on blur. --- src/helpers/web/WebPanelServer.cpp | 53 ++++++++++-------------------- 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/src/helpers/web/WebPanelServer.cpp b/src/helpers/web/WebPanelServer.cpp index 5f107214..0e6828c3 100644 --- a/src/helpers/web/WebPanelServer.cpp +++ b/src/helpers/web/WebPanelServer.cpp @@ -866,39 +866,10 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
- + + - +
@@ -2150,6 +2121,8 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML( } function refreshMqttIataWarning() { const input = document.getElementById("mqttIata"); + const saveBtn = document.querySelector('[data-input="mqttIata"][data-prefix]'); + if (saveBtn) saveBtn.disabled = !input || !input.value.trim(); const banner = document.getElementById("mqttIataBanner"); const inlineWarning = document.getElementById("mqttBrokerWarning"); const showWarning = !!(input && isUnsetMqttIata(input.value)); @@ -2402,6 +2375,13 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML( } selectEl.innerHTML = options.join(""); syncRadioPresetUi(); + const datalist = document.getElementById("mqttIataList"); + const iataOptions = ['']; + for (const entry of entries) { + if (!entry.code || !entry.name) continue; + iataOptions.push(``); + } + datalist.innerHTML = iataOptions.join(""); } catch (error) { radioPresetEntries = []; selectEl.innerHTML = ''; @@ -2453,9 +2433,12 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML( document.querySelectorAll("[data-cmd]").forEach((btn) => btn.onclick = () => runCommand(btn.dataset.cmd)); document.querySelectorAll("[data-prefix]").forEach((btn) => btn.onclick = () => runPrefixed(btn.dataset.prefix, btn.dataset.input)); document.querySelectorAll("[data-load-cmd]").forEach((btn) => btn.onclick = () => loadField(btn.dataset.loadCmd, btn.dataset.loadInput, btn.dataset.loadFormat)); - const mqttIataSelect = document.getElementById("mqttIata"); - if (mqttIataSelect) { - mqttIataSelect.addEventListener("change", refreshMqttIataWarning); + const mqttIataInput = document.getElementById("mqttIata"); + if (mqttIataInput) { + mqttIataInput.addEventListener("input", () => { + const saveBtn = document.querySelector('[data-input="mqttIata"][data-prefix]'); + if (saveBtn) saveBtn.disabled = !mqttIataInput.value.trim(); + }); } async function setMeshcoretelMode(enabled) { if (enabled && getLetsmeshMode() === "both") {