From 195635006a1fcdd257d6963ee0c46c3b9fa03063 Mon Sep 17 00:00:00 2001 From: Giorgio Ravera Date: Wed, 3 Jun 2026 23:01:14 +0200 Subject: [PATCH] Added spin to reloadDhcp, reloadDns, restart buttons --- frontend/aliases.html | 34 ++++++++++++++++++----- frontend/css/layout.css | 13 +++++++++ frontend/devices.html | 34 ++++++++++++++++++----- frontend/hosts.html | 34 ++++++++++++++++++----- frontend/js/aliases.js | 38 ++++++++++++------------- frontend/js/common.js | 56 +++++++++++++++++++++++++++++++++++++ frontend/js/devices.js | 38 ++++++++++++------------- frontend/js/hosts.js | 38 ++++++++++++------------- frontend/js/leases.js | 38 ++++++++++++------------- frontend/js/settings.js | 61 +++++++++++++++++++++-------------------- frontend/leases.html | 34 +++++++++++++++++++---- frontend/settings.html | 6 ++-- 12 files changed, 281 insertions(+), 143 deletions(-) diff --git a/frontend/aliases.html b/frontend/aliases.html index efd4a5d..a0033d4 100644 --- a/frontend/aliases.html +++ b/frontend/aliases.html @@ -91,18 +91,38 @@ -
- - -
diff --git a/frontend/css/layout.css b/frontend/css/layout.css index 2e990f0..74f299f 100644 --- a/frontend/css/layout.css +++ b/frontend/css/layout.css @@ -573,6 +573,19 @@ td.actions { transform: translateY(-1px); } +/* ================================ + Spin + ================================ */ +.spin { + display: inline-block; + animation: spin 1s linear infinite; +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + /* ================================ Responsive ================================ */ diff --git a/frontend/devices.html b/frontend/devices.html index 1153377..bf5f748 100644 --- a/frontend/devices.html +++ b/frontend/devices.html @@ -91,18 +91,38 @@ -
- - -
diff --git a/frontend/hosts.html b/frontend/hosts.html index 5707b43..b4d83a4 100644 --- a/frontend/hosts.html +++ b/frontend/hosts.html @@ -91,18 +91,38 @@ -
- - -
diff --git a/frontend/js/aliases.js b/frontend/js/aliases.js index ed3754d..5096958 100644 --- a/frontend/js/aliases.js +++ b/frontend/js/aliases.js @@ -1,5 +1,5 @@ // Import common js -import { loadModals, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch } from './common.js'; +import { loadModals, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch, handleReload } from './common.js'; // Import services import { serviceReloadDNS, serviceReloadDHCP, serviceGetAliases, serviceGetAlias, serviceCreateAlias, serviceUpdateAlias, serviceDeleteAlias } from './services.js'; @@ -422,28 +422,24 @@ const actionHandlers = { // handled by bootstrap modal show event }, // Reload DNS - reloadDns: async () => { - try { - const result = await serviceReloadDNS(); - const msg = (typeof result === 'object' && result?.message) - ? result.message - : 'DNS reload successfully'; - showToast(msg, true); - } catch (err) { - showToast(err?.message || "Error reloading DNS", false); - } + reloadDns: async (e, el) => { + await handleReload( + el, + serviceReloadDNS, + "DNS reload successfully", + "Error reloading DNS", + "Reloading DNS..." + ); }, // Reload DHCP - reloadDhcp: async () => { - try { - const result = await serviceReloadDHCP(); - const msg = (typeof result === 'object' && result?.message) - ? result.message - : 'DHCP reload successfully'; - showToast(msg, true); - } catch (err) { - showToast(err?.message || "Error reloading DHCP", false); - } + reloadDhcp: async (e, el) => { + await handleReload( + el, + serviceReloadDHCP, + "DHCP reload successfully", + "Error reloading DHCP", + "Reloading DHCP..." + ); }, }; diff --git a/frontend/js/common.js b/frontend/js/common.js index a2a4b83..6f73280 100644 --- a/frontend/js/common.js +++ b/frontend/js/common.js @@ -390,3 +390,59 @@ export function showConfirmModal(message = "Are you sure?") { modal.show(); }); } + +/** + * Generic handler for reload actions with UI feedback. + * Disables the button, shows a spinner, executes the async service, + * then restores the button and shows a toast message. + * + * @param {HTMLElement} button + * @param {Function} serviceFn + * @param {string} defaultSuccessMsg + * @param {string} defaultErrorMsg + * @param {string} [workingText] + */ +/** + * Generic handler for reload actions with UI feedback. + */ +export async function handleReload( + button, + serviceFn, + defaultSuccessMsg, + defaultErrorMsg, + workingText = "Working...", + keepDisabled = false +) { + + if (!button || button.disabled) return; + + button.disabled = true; + + const originalHTML = button.innerHTML; + + // spinner + testo + button.innerHTML = ` + + ${workingText} + `; + + try { + const result = await serviceFn(); + + const msg = + (result && typeof result === "object" && result.message) + ? result.message + : defaultSuccessMsg; + + showToast(msg, true); + + } catch (err) { + showToast(err?.message || defaultErrorMsg, false); + + } finally { + if (!keepDisabled) { + button.disabled = false; + button.innerHTML = originalHTML; + } + } +} diff --git a/frontend/js/devices.js b/frontend/js/devices.js index 377c2c3..0d7779a 100644 --- a/frontend/js/devices.js +++ b/frontend/js/devices.js @@ -1,5 +1,5 @@ // Import common js -import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch } from './common.js'; +import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch, handleReload } from './common.js'; // Import services import { serviceReloadDNS, serviceReloadDHCP, serviceGetDHCPLeases, serviceGetDHCPLease, serviceDeleteDHCPLease, serviceGetDevices, serviceGetHost, serviceCreateHost, serviceUpdateHost, serviceDeleteHost } from './services.js'; @@ -551,28 +551,24 @@ const actionHandlers = { // handled by bootstrap modal show event }, // Reload DNS - reloadDns: async () => { - try { - const result = await serviceReloadDNS(); - const msg = (typeof result === 'object' && result?.message) - ? result.message - : 'DNS reload successfully'; - showToast(msg, true); - } catch (err) { - showToast(err?.message || "Error reloading DNS", false); - } + reloadDns: async (e, el) => { + await handleReload( + el, + serviceReloadDNS, + "DNS reload successfully", + "Error reloading DNS", + "Reloading DNS..." + ); }, // Reload DHCP - reloadDhcp: async () => { - try { - const result = await serviceReloadDHCP(); - const msg = (typeof result === 'object' && result?.message) - ? result.message - : 'DHCP reload successfully'; - showToast(msg, true); - } catch (err) { - showToast(err?.message || "Error reloading DHCP", false); - } + reloadDhcp: async (e, el) => { + await handleReload( + el, + serviceReloadDHCP, + "DHCP reload successfully", + "Error reloading DHCP", + "Reloading DHCP..." + ); }, }; diff --git a/frontend/js/hosts.js b/frontend/js/hosts.js index b35b2e5..4ce2932 100644 --- a/frontend/js/hosts.js +++ b/frontend/js/hosts.js @@ -1,5 +1,5 @@ // Import common js -import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch } from './common.js'; +import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch, handleReload } from './common.js'; // Import services import { serviceReloadDNS, serviceReloadDHCP, serviceGetHosts, serviceGetHost, serviceCreateHost, serviceUpdateHost, serviceDeleteHost } from './services.js'; @@ -447,28 +447,24 @@ const actionHandlers = { // handled by bootstrap modal show event }, // Reload DNS - reloadDns: async () => { - try { - const result = await serviceReloadDNS(); - const msg = (typeof result === 'object' && result?.message) - ? result.message - : 'DNS reload successfully'; - showToast(msg, true); - } catch (err) { - showToast(err?.message || "Error reloading DNS", false); - } + reloadDns: async (e, el) => { + await handleReload( + el, + serviceReloadDNS, + "DNS reload successfully", + "Error reloading DNS", + "Reloading DNS..." + ); }, // Reload DHCP - reloadDhcp: async () => { - try { - const result = await serviceReloadDHCP(); - const msg = (typeof result === 'object' && result?.message) - ? result.message - : 'DHCP reload successfully'; - showToast(msg, true); - } catch (err) { - showToast(err?.message || "Error reloading DHCP", false); - } + reloadDhcp: async (e, el) => { + await handleReload( + el, + serviceReloadDHCP, + "DHCP reload successfully", + "Error reloading DHCP", + "Reloading DHCP..." + ); }, }; diff --git a/frontend/js/leases.js b/frontend/js/leases.js index 1a0144b..3e6b5b6 100644 --- a/frontend/js/leases.js +++ b/frontend/js/leases.js @@ -1,5 +1,5 @@ // Import common js -import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch } from './common.js'; +import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch, handleReload } from './common.js'; // Import services import { serviceReloadDNS, serviceReloadDHCP, serviceGetDHCPLeases, serviceDeleteDHCPLease, serviceGetDHCPLease, serviceCreateHost} from './services.js'; @@ -447,28 +447,24 @@ const actionHandlers = { // handled by bootstrap modal show event }, // Reload DNS - reloadDns: async () => { - try { - const result = await serviceReloadDNS(); - const msg = (typeof result === 'object' && result?.message) - ? result.message - : 'DNS reload successfully'; - showToast(msg, true); - } catch (err) { - showToast(err?.message || "Error reloading DNS", false); - } + reloadDns: async (e, el) => { + await handleReload( + el, + serviceReloadDNS, + "DNS reload successfully", + "Error reloading DNS", + "Reloading DNS..." + ); }, // Reload DHCP - reloadDhcp: async () => { - try { - const result = await serviceReloadDHCP(); - const msg = (typeof result === 'object' && result?.message) - ? result.message - : 'DHCP reload successfully'; - showToast(msg, true); - } catch (err) { - showToast(err?.message || "Error reloading DHCP", false); - } + reloadDhcp: async (e, el) => { + await handleReload( + el, + serviceReloadDHCP, + "DHCP reload successfully", + "Error reloading DHCP", + "Reloading DHCP..." + ); }, }; diff --git a/frontend/js/settings.js b/frontend/js/settings.js index c3cd443..39b5aaa 100644 --- a/frontend/js/settings.js +++ b/frontend/js/settings.js @@ -1,5 +1,5 @@ // Import common js -import { loadModals, showToast, clearSearch, showConfirmModal } from './common.js'; +import { loadModals, showToast, clearSearch, showConfirmModal, handleReload } from './common.js'; // Import services import { serviceGetConfigs, serviceGetConfig, serviceUpdateConfig, serviceResetConfig, serviceRestartApp, serviceIsAlive } from './services.js'; @@ -147,24 +147,27 @@ function collapseAllGroups() { }); } -// ----------------------------- -// Overlay -// ----------------------------- -function showRestartOverlay() { - document.getElementById("restartOverlay") - ?.classList.remove("d-none"); -} - // ----------------------------- // Polling to check if the system restarted properly // ----------------------------- -function startReconnectPolling() { +function startReconnectPolling(button, originalHtmlButton) { - // overlay (opzionale ma consigliato) - showRestartOverlay(); + if (!button) return; + + const maxAttempts = 30; // 1 minuto + let attempts = 0; const interval = setInterval(async () => { + attempts++; + if (attempts > maxAttempts) { + clearInterval(interval); + showToast("Server did not come back online", false); + button.innerHTML = originalHtmlButton; + button.disabled = false; + return; + } + try { // prova endpoint leggero const isUp = await serviceIsAlive(); @@ -177,8 +180,6 @@ function startReconnectPolling() { setTimeout(() => location.reload(), 500); - const btn = document.getElementById("restartBtn"); - btn?.removeAttribute("disabled"); } } catch (err) { console.log("Waiting for server..."); @@ -190,25 +191,23 @@ function startReconnectPolling() { // ----------------------------- // Restart application // ----------------------------- -async function restartApp() { - +async function restartApp(button) { const confirmed = await showConfirmModal("Restart the application?"); if (!confirmed) return; - const btn = document.getElementById("restartBtn"); - btn?.setAttribute("disabled", "true"); - - try { - await serviceRestartApp(); - showToast("Application is restarting...", true); + const originalHtmlButton = button.innerHTML; - // wait restart - startReconnectPolling(); + const ok = await handleReload( + button, + serviceRestartApp, + "Application is restarting...", + "Error restarting application", + "Restarting...", + true + ); - } catch (err) { - console.error(err?.message || "Error restarting application"); - showToast(err?.message || "Error restarting application", false); - btn?.removeAttribute("disabled"); + if (ok !== false) { + startReconnectPolling(button, originalHtmlButton); } } @@ -694,6 +693,10 @@ const actionHandlers = { edit: () => { // handled by bootstrap modal show event }, + // Reload DHCP + restartApp: async (e, el) => { + await restartApp(el) + }, } // ----------------------------- @@ -826,8 +829,6 @@ function initEvents() { ?.addEventListener("click", expandAllGroups); document.getElementById("collapseAllBtn") ?.addEventListener("click", collapseAllGroups); - document.getElementById("restartBtn") - ?.addEventListener("click", restartApp); } // ----------------------------- diff --git a/frontend/leases.html b/frontend/leases.html index c29f980..14afc31 100644 --- a/frontend/leases.html +++ b/frontend/leases.html @@ -91,14 +91,38 @@ -
- + + +
+ + + -
diff --git a/frontend/settings.html b/frontend/settings.html index 68347f2..ac281b8 100644 --- a/frontend/settings.html +++ b/frontend/settings.html @@ -115,10 +115,10 @@
- -- 2.47.3