]> git.giorgioravera.it Git - network-manager.git/commitdiff
Added spin to reloadDhcp, reloadDns, restart buttons
authorGiorgio Ravera <giorgio.ravera@gmail.com>
Wed, 3 Jun 2026 21:01:14 +0000 (23:01 +0200)
committerGiorgio Ravera <giorgio.ravera@gmail.com>
Wed, 3 Jun 2026 21:01:14 +0000 (23:01 +0200)
12 files changed:
frontend/aliases.html
frontend/css/layout.css
frontend/devices.html
frontend/hosts.html
frontend/js/aliases.js
frontend/js/common.js
frontend/js/devices.js
frontend/js/hosts.js
frontend/js/leases.js
frontend/js/settings.js
frontend/leases.html
frontend/settings.html

index efd4a5da8254229974c05c70dbe7df8e9b1a3853..a0033d473425bfb6eada932170ab19443fbe2fde 100644 (file)
                 </div>
 
                 <!-- Bottoni -->
-                <div class="col-12 col-md-auto d-flex gap-2 flex-wrap">
-                    <button class="btn btn-primary" title="Add Alias" aria-label="Add Alias"
+                <div class="col-12 col-md-auto d-flex align-items-center gap-2 flex-wrap">
+
+                    <!-- Add alias -->
+                    <button
+                            class="btn btn-primary"
+                            title="Add Alias"
+                            aria-label="Add Alias"
                             data-bs-toggle="modal" data-bs-target="#addAliasModal">
-                        <i class="bi bi-plus-lg"></i><span class="label"> Add Alias</span>
+                        <i class="bi bi-plus-lg"></i>
+                        <span class="label"> Add Alias</span>
                     </button>
-                    <button class="btn btn-primary" title="Reload DNS (BIND)" aria-label="Reload DNS"
+
+                    <!-- Separator -->
+                    <div class="vr mx-2"></div>
+
+                    <!-- Reload DNS -->
+                    <button
+                            class="btn btn-primary"
+                            title="Reload DNS (BIND)"
+                            aria-label="Reload DNS"
                             data-action="reloadDns">
-                        <i class="bi bi-arrow-repeat"></i><span class="label"> Reload DNS</span>
+                        <i class="bi bi-arrow-repeat"></i>
+                        <span class="label"> Reload DNS</span>
                     </button>
-                    <button class="btn btn-primary" title="Reload DHCP (Kea)" aria-label="Reload DHCP"
+
+                    <!-- Reload DNS -->
+                    <button class="btn btn-primary"
+                            title="Reload DHCP (Kea)"
+                            aria-label="Reload DHCP"
                             data-action="reloadDhcp">
-                        <i class="bi bi-arrow-repeat"></i><span class="label"> Reload DHCP</span>
+                        <i class="bi bi-arrow-repeat"></i>
+                        <span class="label"> Reload DHCP</span>
                     </button>
                 </div>
             </div>
index 2e990f09272422cf2dd31429819a4b89e080a761..74f299f93cafae9f56116acc933a67122624493f 100644 (file)
@@ -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
    ================================ */
index 115337769067c923c56e5f88e9bf49ba7d574e97..bf5f748d05e158c295171c20364532a6c867bf2b 100644 (file)
                 </div>
 
                 <!-- Bottoni -->
-                <div class="col-12 col-md-auto d-flex gap-2 flex-wrap">
-                    <button class="btn btn-primary" title="Add Host" aria-label="Add Host"
+                <div class="col-12 col-md-auto d-flex align-items-center gap-2 flex-wrap">
+
+                    <!-- Add host -->
+                    <button
+                            class="btn btn-primary"
+                            title="Add Host"
+                            aria-label="Add Host"
                             data-bs-toggle="modal" data-bs-target="#addHostModal">
-                        <i class="bi bi-plus-lg"></i><span class="label"> Add Host</span>
+                        <i class="bi bi-plus-lg"></i>
+                        <span class="label"> Add Host</span>
                     </button>
-                    <button class="btn btn-primary" title="Reload DNS (BIND)" aria-label="Reload DNS"
+
+                    <!-- Separator -->
+                    <div class="vr mx-2"></div>
+
+                    <!-- Reload DNS -->
+                    <button
+                            class="btn btn-primary"
+                            title="Reload DNS (BIND)"
+                            aria-label="Reload DNS"
                             data-action="reloadDns">
-                        <i class="bi bi-arrow-repeat"></i><span class="label"> Reload DNS</span>
+                        <i class="bi bi-arrow-repeat"></i>
+                        <span class="label"> Reload DNS</span>
                     </button>
-                    <button class="btn btn-primary" title="Reload DHCP (Kea)" aria-label="Reload DHCP"
+
+                    <!-- Reload DNS -->
+                    <button class="btn btn-primary"
+                            title="Reload DHCP (Kea)"
+                            aria-label="Reload DHCP"
                             data-action="reloadDhcp">
-                        <i class="bi bi-arrow-repeat"></i><span class="label"> Reload DHCP</span>
+                        <i class="bi bi-arrow-repeat"></i>
+                        <span class="label"> Reload DHCP</span>
                     </button>
                 </div>
             </div>
index 5707b4340d45b4bdeea2ccca5733e6eda3123a9e..b4d83a470d8e85ed58a3543a68c3c197aa7d9877 100644 (file)
                 </div>
 
                 <!-- Bottoni -->
-                <div class="col-12 col-md-auto d-flex gap-2 flex-wrap">
-                    <button class="btn btn-primary" title="Add Host" aria-label="Add Host"
+                <div class="col-12 col-md-auto d-flex align-items-center gap-2 flex-wrap">
+
+                    <!-- Add host -->
+                    <button
+                            class="btn btn-primary"
+                            title="Add Host"
+                            aria-label="Add Host"
                             data-bs-toggle="modal" data-bs-target="#addHostModal">
-                        <i class="bi bi-plus-lg"></i><span class="label"> Add Host</span>
+                        <i class="bi bi-plus-lg"></i>
+                        <span class="label"> Add Host</span>
                     </button>
-                    <button class="btn btn-primary" title="Reload DNS (BIND)" aria-label="Reload DNS"
+
+                    <!-- Separator -->
+                    <div class="vr mx-2"></div>
+
+                    <!-- Reload DNS -->
+                    <button
+                            class="btn btn-primary"
+                            title="Reload DNS (BIND)"
+                            aria-label="Reload DNS"
                             data-action="reloadDns">
-                        <i class="bi bi-arrow-repeat"></i><span class="label"> Reload DNS</span>
+                        <i class="bi bi-arrow-repeat"></i>
+                        <span class="label"> Reload DNS</span>
                     </button>
-                    <button class="btn btn-primary" title="Reload DHCP (Kea)" aria-label="Reload DHCP"
+
+                    <!-- Reload DNS -->
+                    <button class="btn btn-primary"
+                            title="Reload DHCP (Kea)"
+                            aria-label="Reload DHCP"
                             data-action="reloadDhcp">
-                        <i class="bi bi-arrow-repeat"></i><span class="label"> Reload DHCP</span>
+                        <i class="bi bi-arrow-repeat"></i>
+                        <span class="label"> Reload DHCP</span>
                     </button>
                 </div>
             </div>
index ed3754d6d1386ed004d979e1eb8dfc722be94da9..50969581fd84cc2a4d4349d455e52da8fc8ff6ea 100644 (file)
@@ -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..."
+        );
     },
 };
 
index a2a4b83936d55677b08ce15c0b9b32a0fff8ff6f..6f73280dfbda1bc0ad158102a1e403938ceae211 100644 (file)
@@ -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 = `
+        <i class="bi bi-arrow-repeat spin"></i>
+        <span>${workingText}</span>
+    `;
+
+    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;
+        }
+    }
+}
index 377c2c35405f0b0c9a79ee9df37804eaaff414f3..0d7779a662c67d133995f63f06238cbea2919451 100644 (file)
@@ -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..."
+        );
     },
 };
 
index b35b2e513fa6815f5196115e9376b4444c1be1de..4ce2932714ac74eba94e6151dfcbc1227e9d1f92 100644 (file)
@@ -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..."
+        );
     },
 };
 
index 1a0144bbff16c8da217bad5d2600845372548390..3e6b5b6d692a982b0746783192877ee58d9aea05 100644 (file)
@@ -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..."
+        );
     },
 };
 
index c3cd443250b2ee691d46fe4db9655b8bc67ac373..39b5aaa0cfafc4bc73115244121b003ea277ca08 100644 (file)
@@ -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);
 }
 
 // -----------------------------
index c29f98099def07e11fc475a51f5a684ab9de3023..14afc31e0d48fd2fe9830034da2f4ae87135aa37 100644 (file)
                 </div>
 
                 <!-- Bottoni -->
-                <div class="col-12 col-md-auto d-flex gap-2 flex-wrap">
-                    <button class="btn btn-primary" title="Reload DNS (BIND)" aria-label="Reload DNS"
+                <div class="col-12 col-md-auto d-flex align-items-center gap-2 flex-wrap">
+
+                    <!-- Add host -->
+                    <button
+                            class="btn btn-primary"
+                            title="Add Host"
+                            aria-label="Add Host"
+                            data-bs-toggle="modal" data-bs-target="#addHostModal">
+                        <i class="bi bi-plus-lg"></i>
+                        <span class="label"> Add Host</span>
+                    </button>
+
+                    <!-- Separator -->
+                    <div class="vr mx-2"></div>
+
+                    <!-- Reload DNS -->
+                    <button
+                            class="btn btn-primary"
+                            title="Reload DNS (BIND)"
+                            aria-label="Reload DNS"
                             data-action="reloadDns">
-                        <i class="bi bi-arrow-repeat"></i><span class="label"> Reload DNS</span>
+                        <i class="bi bi-arrow-repeat"></i>
+                        <span class="label"> Reload DNS</span>
                     </button>
-                    <button class="btn btn-primary" title="Reload DHCP (Kea)" aria-label="Reload DHCP"
+
+                    <!-- Reload DNS -->
+                    <button class="btn btn-primary"
+                            title="Reload DHCP (Kea)"
+                            aria-label="Reload DHCP"
                             data-action="reloadDhcp">
-                        <i class="bi bi-arrow-repeat"></i><span class="label"> Reload DHCP</span>
+                        <i class="bi bi-arrow-repeat"></i>
+                        <span class="label"> Reload DHCP</span>
                     </button>
                 </div>
             </div>
index 68347f28702ecd8971628d9e2cdbb59dc353740e..ac281b897acbf8698951492000781e6861b74ab9 100644 (file)
                     <div class="vr mx-2"></div>
 
                     <!-- Restart -->
-                    <button id="restartBtn"
-                            class="btn btn-outline-danger d-flex align-items-center gap-2"
+                    <button class="btn btn-outline-danger d-flex align-items-center gap-2"
                             title="Restart system"
-                            aria-label="Restart system">
+                            aria-label="Restart system"
+                            data-action="restartApp">
                         <i class="bi bi-arrow-repeat"></i>
                         <span>Restart</span>
                     </button>