]> git.giorgioravera.it Git - network-manager.git/commitdiff
Different searchbard for mobile and deskop
authorGiorgio Ravera <giorgio.ravera@gmail.com>
Tue, 9 Jun 2026 13:06:17 +0000 (15:06 +0200)
committerGiorgio Ravera <giorgio.ravera@gmail.com>
Tue, 9 Jun 2026 13:06:17 +0000 (15:06 +0200)
frontend/aliases.html
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 303b02b9100da094baf61fac23983182947747dc..f957687f9160b42322529274b6dcdefc8334b0d7 100644 (file)
@@ -66,6 +66,7 @@
     <section class="page-frame">
         <div class="container-fluid p-0">
             <div class="row g-2 align-items-center">
+
                 <!-- Title -->
                 <div class="col-12 col-md-auto">
                     <h2 class="mb-0 d-flex align-items-center gap-2 lh-1">
                 <!-- Spacer -->
                 <div class="col d-none d-md-block"></div>
 
-                <!-- Search -->
-                <div class="col-12 col-md-auto">
+                <!-- Search Desktop -->
+                <div class="col-12 col-md-auto d-none d-md-block">
                     <div class="search-wrapper">
                         <input
                             type="text"
                             id="searchInput"
                             placeholder="Search..."
                             class="form-control form-control-sm placeholder-italic"
-                            aria-label="Search alias"
-                        >
+                            aria-label="Search alias">
                     </div>
                 </div>
 
                 <!-- Bottoni -->
                 <div class="col-12 col-md-auto d-flex align-items-center gap-2 flex-wrap">
 
+                    <!-- Search Mobile -->
+                    <div class="col-auto d-md-none">
+                        <button
+                                class="btn btn-primary d-flex align-items-center gap-2 px-3"
+                                data-bs-toggle="collapse" data-bs-target="#mobileSearch"
+                                aria-expanded="false" aria-controls="mobileSearch">
+                            <i class="bi bi-search"></i>
+                        </button>
+                    </div>
+
+                    <!-- Separator -->
+                    <div class="vr mx-2"></div>
+
                     <!-- Add alias -->
                     <button
                             class="btn btn-primary d-flex align-items-center gap-2 px-3"
                     </button>
                 </div>
             </div>
+
+            <!-- Search collapsabile -->
+            <div class="collapse mt-2 d-md-none" id="mobileSearch">
+                <input
+                    type="text"
+                    id="searchInputMobile"
+                    placeholder="Search..."
+                    class="form-control form-control-sm placeholder-italic"
+                    aria-label="Search alias">
+            </div>
         </div>
     </section>
 
         <table id="dataTable" class="table table-bordered table-hover align-middle">
             <thead class="table-light">
                 <tr>
-                    <th data-type="string" data-sortable="true"  data-sort="0">Alias      <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="true"  data-sort="1">Target     <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="true"  data-sort="2">Description<span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="true"  data-sort="3">Options    <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="false" data-sort="4">Actions    <span class="sort-arrow" aria-hidden="true"></span></th>
+                    <th data-type="string" data-sortable="true" data-sort="0">
+                        Alias <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="true" data-sort="1">
+                        Target <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="true" data-sort="2">
+                        Description <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="true" data-sort="3" class="text-center text-nowrap">
+                        Options <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="false" class="text-center text-nowrap">
+                        Actions <span class="sort-arrow invisible" aria-hidden="true"></span>
+                    </th>
                 </tr>
             </thead>
             <tbody></tbody>
index 6ca3e36beaae7954f85a38774155864de36058fd..00093579e8b9397889bd4f4ff77874a553527913 100644 (file)
@@ -66,6 +66,7 @@
     <section class="page-frame">
         <div class="container-fluid p-0">
             <div class="row g-2 align-items-center">
+
                 <!-- Title -->
                 <div class="col-12 col-md-auto">
                     <h2 class="mb-0 d-flex align-items-center gap-2 lh-1">
                 <!-- Spacer -->
                 <div class="col d-none d-md-block"></div>
 
-                <!-- Search -->
-                <div class="col-12 col-md-auto">
+                <!-- Search Desktop -->
+                <div class="col-12 col-md-auto d-none d-md-block">
                     <div class="search-wrapper">
                         <input
                             type="text"
                             id="searchInput"
                             placeholder="Search..."
                             class="form-control form-control-sm placeholder-italic"
-                            aria-label="Search hosts"
-                        >
+                            aria-label="Search device">
                     </div>
                 </div>
 
                 <!-- Bottoni -->
                 <div class="col-12 col-md-auto d-flex align-items-center gap-2 flex-wrap">
 
+                    <!-- Search Mobile -->
+                    <div class="col-auto d-md-none">
+                        <button
+                                class="btn btn-primary d-flex align-items-center gap-2 px-3"
+                                data-bs-toggle="collapse" data-bs-target="#mobileSearch"
+                                aria-expanded="false" aria-controls="mobileSearch">
+                            <i class="bi bi-search"></i>
+                        </button>
+                    </div>
+
+                    <!-- Separator -->
+                    <div class="vr mx-2"></div>
+
                     <!-- Add host -->
                     <button
                             class="btn btn-primary d-flex align-items-center gap-2 px-3"
                     </button>
                 </div>
             </div>
+
+            <!-- Search collapsabile -->
+            <div class="collapse mt-2 d-md-none" id="mobileSearch">
+                <input
+                    type="text"
+                    id="searchInputMobile"
+                    placeholder="Search..."
+                    class="form-control form-control-sm placeholder-italic"
+                    aria-label="Search device">
+            </div>
         </div>
     </section>
 
         <table id="dataTable" class="table table-bordered table-hover align-middle">
             <thead class="table-light">
                 <tr>
-                    <th data-type="ipv4"   data-sortable="true" data-sort="0">IP Address <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="mac"    data-sortable="true" data-sort="1">MAC Address<span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="true" data-sort="2">Hostname   <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="true" data-sort="3">Description<span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="true" data-sort="4">State      <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="true" data-sort="5">Active     <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="false" data-sort="6">Actions   <span class="sort-arrow" aria-hidden="true"></span></th>
+                    <th data-type="ipv4" data-sortable="true" data-sort="0">
+                        IP Address <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="mac" data-sortable="true" data-sort="1">
+                        MAC Address <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="true" data-sort="2">
+                        Hostname <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="true" data-sort="3">
+                        Description <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="true" data-sort="4" class="text-center text-nowrap">
+                        State <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="true" data-sort="5" class="text-center text-nowrap">
+                        Active <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="false" class="text-center text-nowrap">
+                        Actions <span class="sort-arrow invisible" aria-hidden="true"></span>
+                    </th>
                 </tr>
             </thead>
             <tbody></tbody>
index 81951980dc32f5e44e2e9069f9e451879aca7412..15a287582402545042f1ed73fb71d2cf384517c1 100644 (file)
@@ -66,6 +66,7 @@
     <section class="page-frame">
         <div class="container-fluid p-0">
             <div class="row g-2 align-items-center">
+
                 <!-- Title -->
                 <div class="col-12 col-md-auto">
                     <h2 class="mb-0 d-flex align-items-center gap-2 lh-1">
                 <!-- Spacer -->
                 <div class="col d-none d-md-block"></div>
 
-                <!-- Search -->
-                <div class="col-12 col-md-auto">
+                <!-- Search Desktop -->
+                <div class="col-12 col-md-auto d-none d-md-block">
                     <div class="search-wrapper">
                         <input
                             type="text"
                             id="searchInput"
                             placeholder="Search..."
                             class="form-control form-control-sm placeholder-italic"
-                            aria-label="Search hosts"
-                        >
+                            aria-label="Search hosts">
                     </div>
                 </div>
 
                 <!-- Bottoni -->
                 <div class="col-12 col-md-auto d-flex align-items-center gap-2 flex-wrap">
 
+                    <!-- Search Mobile -->
+                    <div class="col-auto d-md-none">
+                        <button
+                                class="btn btn-primary d-flex align-items-center gap-2 px-3"
+                                data-bs-toggle="collapse" data-bs-target="#mobileSearch"
+                                aria-expanded="false" aria-controls="mobileSearch">
+                            <i class="bi bi-search"></i>
+                        </button>
+                    </div>
+
+                    <!-- Separator -->
+                    <div class="vr mx-2"></div>
+
                     <!-- Add host -->
                     <button
                             class="btn btn-primary d-flex align-items-center gap-2 px-3"
                     </button>
                 </div>
             </div>
+
+            <!-- Search collapsabile -->
+            <div class="collapse mt-2 d-md-none" id="mobileSearch">
+                <input
+                    type="text"
+                    id="searchInputMobile"
+                    placeholder="Search..."
+                    class="form-control form-control-sm placeholder-italic"
+                    aria-label="Search hosts">
+            </div>
         </div>
     </section>
 
         <table id="dataTable" class="table table-bordered table-hover align-middle">
             <thead class="table-light">
                 <tr>
-                    <th data-type="string" data-sortable="true"  data-sort="0">Hostname    <span class="sort-arrow"></span></th>
-                    <th data-type="ipv4"   data-sortable="true"  data-sort="1">IP Address  <span class="sort-arrow"></span></th>
-                    <th data-type="mac"    data-sortable="true"  data-sort="2">MAC Address <span class="sort-arrow"></span></th>
-                    <th data-type="string" data-sortable="true"  data-sort="3">Description <span class="sort-arrow"></span></th>
-                    <th data-type="string" data-sortable="true"  data-sort="4">Options     <span class="sort-arrow"></span></th>
-                    <th data-type="string" data-sortable="false" data-sort="5">Actions     <span class="sort-arrow"></span></th>
+                    <th data-type="string" data-sortable="true" data-sort="0">
+                        Hostname <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="ipv4" data-sortable="true" data-sort="1">
+                        IP Address <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="mac" data-sortable="true" data-sort="2">
+                        MAC Address <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="true" data-sort="3">
+                        Description <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="true" data-sort="4" class="text-center text-nowrap">
+                        Options <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="false" class="text-center text-nowrap">
+                        Actions <span class="sort-arrow invisible" aria-hidden="true"></span>
+                    </th>
                 </tr>
             </thead>
             <tbody></tbody>
index a5a0cd0dcd112db2813b572d81e82f40e40114e6..042d0b1159f32ab3e5e61323091d0e094c41cbd1 100644 (file)
@@ -1,5 +1,5 @@
 // Import common js
-import { loadModals, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch, handleReload } from './common.js';
+import { loadModals, showToast, sortTable, initSortableTable, resetSorting, handleSearch, filterTable, clearSearch, handleReload } from './common.js';
 // Import services
 import { serviceReloadDNS, serviceReloadDHCP, serviceGetAliases, serviceGetAlias, serviceCreateAlias, serviceUpdateAlias, serviceDeleteAlias } from './services.js';
 
@@ -231,12 +231,11 @@ function updateTable () {
     tableWrapper.classList.remove("d-none");
 
     // apply current search filter
-    const searchInput = document.getElementById("searchInput");
-    if (searchInput) {
-        const term = searchInput.value.trim().toLowerCase();
-        if (term) {
-            filterTable(term);
-        }
+    const term =
+        document.getElementById('searchInput')?.value ||
+        document.getElementById('searchInputMobile')?.value;
+    if (term) {
+        handleSearch(term);
     }
 }
 
@@ -490,16 +489,20 @@ function initUI() {
 // SEARCH
 // -----------------------------
 function initSearch() {
-    // search bar
-    const input = document.getElementById("searchInput");
-    if (!input) return;
-
-    // clean input on load
-    input.value = "";
-    // live filter for each keystroke
-    input.addEventListener("input", (e) => {
-        const term = e.target.value.trim().toLowerCase();
-        filterTable(term);
+
+    ["searchInput", "searchInputMobile"].forEach(id => {
+
+        const input = document.getElementById(id);
+
+        if (!input) return;
+
+        // clean input on load
+        input.value = "";
+
+        // live filter
+        input.addEventListener("input", (e) => {
+            handleSearch(e.target.value);
+        });
     });
 }
 
@@ -608,7 +611,8 @@ function handleKeyboard(e) {
         const tag = (e.target.tagName || "").toLowerCase();
         const isTypingField =
             tag === "input" || tag === "textarea" || tag === "select" || e.target.isContentEditable;
-        const isSearchInput = e.target.id === "searchInput";
+        const isSearchInput =
+            e.target.id === "searchInput" || e.target.id === "searchInputMobile";
 
         // ESC should clear search and reset sorting only if not focused on a typing field, or if focused on the search input (to allow quick clearing of search)
         if (!isTypingField || isSearchInput) {
index 6f73280dfbda1bc0ad158102a1e403938ceae211..f77e2785944f6116ac5704649e572d258a8db811 100644 (file)
@@ -329,6 +329,21 @@ export function initSortableTable() {
     ths.forEach(th => th.setAttribute("aria-sort", "none"));
 }
 
+/**
+  * search handler
+ */
+export function handleSearch(value, callback) {
+    const desktopSearch = document.getElementById('searchInput');
+    const mobileSearch  = document.getElementById('searchInputMobile');
+
+    // sync valori
+    desktopSearch.value = value;
+    mobileSearch.value = value;
+
+    const term = value.trim().toLowerCase();
+    callback(term);
+}
+
 /**
   * Live filter: mostra solo le righe che contengono il testo di ricerca (case-insensitive)
  */
@@ -345,10 +360,15 @@ export function filterTable(term) {
  * Clear search input and reset table filter
  */
 export function clearSearch() {
-    const input = document.getElementById("searchInput");
-    if (input) {
-        input.value = "";
-        input.blur();
+    const desktopSearch = document.getElementById("searchInput");
+    if (desktopSearch) {
+        desktopSearch.value = "";
+        desktopSearch.blur();
+    }
+    const mobileSearch = document.getElementById("searchInputMobile");
+    if (mobileSearch) {
+        mobileSearch.value = "";
+        mobileSearch.blur();
     }
 }
 
index ad00407657e9e3961e225efa14fd94670f8b2e5c..d8273e3c3a200774d8cc564acc43be9f2825afc5 100644 (file)
@@ -1,5 +1,5 @@
 // Import common js
-import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch, handleReload } from './common.js';
+import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, handleSearch, filterTable, clearSearch, handleReload } from './common.js';
 // Import services
 import { serviceReloadDNS, serviceReloadDHCP, serviceGetDHCPLeases, serviceGetDHCPLease, serviceDeleteDHCPLease, serviceGetDevices, serviceGetHost, serviceCreateHost, serviceUpdateHost, serviceDeleteHost } from './services.js';
 
@@ -292,12 +292,11 @@ function updateTable () {
     tableWrapper.classList.remove("d-none");
 
     // apply current search filter
-    const searchInput = document.getElementById("searchInput");
-    if (searchInput) {
-        const term = searchInput.value.trim().toLowerCase();
-        if (term) {
-            filterTable(term);
-        }
+    const term =
+        document.getElementById('searchInput')?.value ||
+        document.getElementById('searchInputMobile')?.value;
+    if (term) {
+        handleSearch(term, filterTable);
     }
 }
 
@@ -619,16 +618,20 @@ function initUI() {
 // SEARCH
 // -----------------------------
 function initSearch() {
-    // search bar
-    const input = document.getElementById("searchInput");
-    if (!input) return;
-
-    // clean input on load
-    input.value = "";
-    // live filter for each keystroke
-    input.addEventListener("input", (e) => {
-        const term = e.target.value.trim().toLowerCase();
-        filterTable(term);
+
+    ["searchInput", "searchInputMobile"].forEach(id => {
+
+        const input = document.getElementById(id);
+
+        if (!input) return;
+
+        // clean input on load
+        input.value = "";
+
+        // live filter
+        input.addEventListener("input", (e) => {
+            handleSearch(e.target.value, filterTable);
+        });
     });
 }
 
@@ -737,7 +740,8 @@ function handleKeyboard(e) {
         const tag = (e.target.tagName || "").toLowerCase();
         const isTypingField =
             tag === "input" || tag === "textarea" || tag === "select" || e.target.isContentEditable;
-        const isSearchInput = e.target.id === "searchInput";
+        const isSearchInput =
+            e.target.id === "searchInput" || e.target.id === "searchInputMobile";
 
         // ESC should clear search and reset sorting only if not focused on a typing field, or if focused on the search input (to allow quick clearing of search)
         if (!isTypingField || isSearchInput) {
index e9b6d6ebca8d37a5b69b7f99bec5dfd17e24b1f0..daf4569187f1fd9fa61525f98fd39777bb22f163 100644 (file)
@@ -1,5 +1,5 @@
 // Import common js
-import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch, handleReload } from './common.js';
+import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, handleSearch, filterTable, clearSearch, handleReload } from './common.js';
 // Import services
 import { serviceReloadDNS, serviceReloadDHCP, serviceGetHosts, serviceGetHost, serviceCreateHost, serviceUpdateHost, serviceDeleteHost } from './services.js';
 
@@ -241,12 +241,11 @@ function updateTable () {
     tableWrapper.classList.remove("d-none");
 
     // apply current search filter
-    const searchInput = document.getElementById("searchInput");
-    if (searchInput) {
-        const term = searchInput.value.trim().toLowerCase();
-        if (term) {
-            filterTable(term);
-        }
+    const term =
+        document.getElementById('searchInput')?.value ||
+        document.getElementById('searchInputMobile')?.value;
+    if (term) {
+        handleSearch(term, filterTable);
     }
 }
 
@@ -515,16 +514,20 @@ function initUI() {
 // SEARCH
 // -----------------------------
 function initSearch() {
-    // search bar
-    const input = document.getElementById("searchInput");
-    if (!input) return;
-
-    // clean input on load
-    input.value = "";
-    // live filter for each keystroke
-    input.addEventListener("input", (e) => {
-        const term = e.target.value.trim().toLowerCase();
-        filterTable(term);
+
+    ["searchInput", "searchInputMobile"].forEach(id => {
+
+        const input = document.getElementById(id);
+
+        if (!input) return;
+
+        // clean input on load
+        input.value = "";
+
+        // live filter
+        input.addEventListener("input", (e) => {
+            handleSearch(e.target.value, filterTable);
+        });
     });
 }
 
@@ -633,7 +636,8 @@ function handleKeyboard(e) {
         const tag = (e.target.tagName || "").toLowerCase();
         const isTypingField =
             tag === "input" || tag === "textarea" || tag === "select" || e.target.isContentEditable;
-        const isSearchInput = e.target.id === "searchInput";
+        const isSearchInput =
+            e.target.id === "searchInput" || e.target.id === "searchInputMobile";
 
         // ESC should clear search and reset sorting only if not focused on a typing field, or if focused on the search input (to allow quick clearing of search)
         if (!isTypingField || isSearchInput) {
index 36b6daac67dd1a6daec47f23cbf1d837f3355663..77504e15ec48ec39235154a7ec05269362da698c 100644 (file)
@@ -1,5 +1,5 @@
 // Import common js
-import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch, handleReload } from './common.js';
+import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, handleSearch, filterTable, clearSearch, handleReload } from './common.js';
 // Import services
 import { serviceReloadDNS, serviceReloadDHCP, serviceGetDHCPLeases, serviceDeleteDHCPLease, serviceGetDHCPLease, serviceCreateHost} from './services.js';
 
@@ -256,12 +256,11 @@ function updateTable () {
     tableWrapper.classList.remove("d-none");
 
     // apply current search filter
-    const searchInput = document.getElementById("searchInput");
-    if (searchInput) {
-        const term = searchInput.value.trim().toLowerCase();
-        if (term) {
-            filterTable(term);
-        }
+    const term =
+        document.getElementById('searchInput')?.value ||
+        document.getElementById('searchInputMobile')?.value;
+    if (term) {
+        handleSearch(term, filterTable);
     }
 }
 
@@ -515,16 +514,20 @@ function initUI() {
 // SEARCH
 // -----------------------------
 function initSearch() {
-    // search bar
-    const input = document.getElementById("searchInput");
-    if (!input) return;
-
-    // clean input on load
-    input.value = "";
-    // live filter for each keystroke
-    input.addEventListener("input", (e) => {
-        const term = e.target.value.trim().toLowerCase();
-        filterTable(term);
+
+    ["searchInput", "searchInputMobile"].forEach(id => {
+
+        const input = document.getElementById(id);
+
+        if (!input) return;
+
+        // clean input on load
+        input.value = "";
+
+        // live filter
+        input.addEventListener("input", (e) => {
+            handleSearch(e.target.value, filterTable);
+        });
     });
 }
 
@@ -625,7 +628,8 @@ function handleKeyboard(e) {
         const tag = (e.target.tagName || "").toLowerCase();
         const isTypingField =
             tag === "input" || tag === "textarea" || tag === "select" || e.target.isContentEditable;
-        const isSearchInput = e.target.id === "searchInput";
+        const isSearchInput =
+            e.target.id === "searchInput" || e.target.id === "searchInputMobile";
 
         // ESC should clear search and reset sorting only if not focused on a typing field, or if focused on the search input (to allow quick clearing of search)
         if (!isTypingField || isSearchInput) {
index a3a42f17e56c13dcd2aff8ce3bb3f0ba749f2bfa..59d2def0a28128af1a52ad9caecc0a69eaae91b0 100644 (file)
@@ -1,5 +1,5 @@
 // Import common js
-import { loadModals, showToast, clearSearch, showConfirmModal, handleReload } from './common.js';
+import { loadModals, showToast, handleSearch, clearSearch, showConfirmModal, handleReload } from './common.js';
 // Import services
 import { serviceGetConfigs, serviceGetConfig, serviceUpdateConfig, serviceResetConfig, serviceRestartApp, serviceIsAlive } from './services.js';
 
@@ -214,12 +214,10 @@ async function restartApp(button) {
 // -----------------------------
 // Update table with current configs
 // -----------------------------
-function updateTable () {
+function updateTable (filter = null) {
     const loader = document.getElementById("loader");
     const tableWrapper = document.getElementById("tableWrapper");
-    const searchInput = document.getElementById("searchInput");
-    const term = searchInput?.value?.trim().toLowerCase();
-    const hasSearch = !!term;
+    const hasSearch = !!filter;
     const expandBtn = document.getElementById("expandAllBtn");
     const collapseBtn = document.getElementById("collapseAllBtn");
 
@@ -280,24 +278,24 @@ function updateTable () {
 
         let rows = grouped[group];
 
-        if (term) {
+        if (filter) {
             rows = rows.filter(c => {
                 return (
-                    (c.key ?? "").toString().toLowerCase().includes(term) ||
-                    (c.value ?? "").toString().toLowerCase().includes(term) ||
-                    (c.description ?? "").toString().toLowerCase().includes(term)
+                    (c.key ?? "").toString().toLowerCase().includes(filter) ||
+                    (c.value ?? "").toString().toLowerCase().includes(filter) ||
+                    (c.description ?? "").toString().toLowerCase().includes(filter)
                 );
             });
         }
 
         // skip group if no result
-        if (term && rows.length === 0) return;
+        if (filter && rows.length === 0) return;
 
         // GROUP HEADER
         tdGroup.colSpan = 7;
         trGroup.className = "table-group-header";
         trGroup.setAttribute("data-group", groupKey);
-        tdGroup.innerHTML = term
+        tdGroup.innerHTML = filter
             ? '<i class="bi bi-folder2-open"></i>'
             : '<i class="bi bi-folder"></i>';
         tdGroup.appendChild(
@@ -315,7 +313,7 @@ function updateTable () {
             tr.setAttribute("data-parent-group", groupKey);
 
             // show only filtered keys
-            if (!term) {
+            if (!filter) {
                 tr.classList.add("d-none");
             }
 
@@ -744,16 +742,20 @@ function initUI() {
 // SEARCH
 // -----------------------------
 function initSearch() {
-    // search bar
-    const input = document.getElementById("searchInput");
-    if (!input) return;
-
-    // clean input on load
-    input.value = "";
-    // live filter for each keystroke
-    input.addEventListener("input", (e) => {
-        const term = e.target.value.trim().toLowerCase();
-        updateTable();
+
+    ["searchInput", "searchInputMobile"].forEach(id => {
+
+        const input = document.getElementById(id);
+
+        if (!input) return;
+
+        // clean input on load
+        input.value = "";
+
+        // live filter
+        input.addEventListener("input", (e) => {
+            handleSearch(e.target.value, updateTable);
+        });
     });
 }
 
@@ -869,7 +871,8 @@ function handleKeyboard(e) {
         const tag = (e.target.tagName || "").toLowerCase();
         const isTypingField =
             tag === "input" || tag === "textarea" || tag === "select" || e.target.isContentEditable;
-        const isSearchInput = e.target.id === "searchInput";
+        const isSearchInput =
+            e.target.id === "searchInput" || e.target.id === "searchInputMobile";
 
         // ESC should clear search only if not focused on a typing field, or if focused on the search input (to allow quick clearing of search)
         if (!isTypingField || isSearchInput) {
index ddcbebeeefcf07051ccca94f0c24dd166a4445f3..31afc21a376e6598816d57d2a2f8d807a712504c 100644 (file)
@@ -66,6 +66,7 @@
     <section class="page-frame">
         <div class="container-fluid p-0">
             <div class="row g-2 align-items-center">
+
                 <!-- Title -->
                 <div class="col-12 col-md-auto">
                     <h2 class="mb-0 d-flex align-items-center gap-2 lh-1">
                 <!-- Spacer -->
                 <div class="col d-none d-md-block"></div>
 
-                <!-- Search -->
-                <div class="col-12 col-md-auto">
+                <!-- Search Desktop -->
+                <div class="col-12 col-md-auto d-none d-md-block">
                     <div class="search-wrapper">
                         <input
                             type="text"
                             id="searchInput"
                             placeholder="Search..."
                             class="form-control form-control-sm placeholder-italic"
-                            aria-label="Search hosts"
-                        >
+                            aria-label="Search lease">
                     </div>
                 </div>
 
                 <!-- Bottoni -->
                 <div class="col-12 col-md-auto d-flex align-items-center gap-2 flex-wrap">
 
+                    <!-- Search Mobile -->
+                    <div class="col-auto d-md-none">
+                        <button
+                                class="btn btn-primary d-flex align-items-center gap-2 px-3"
+                                data-bs-toggle="collapse" data-bs-target="#mobileSearch"
+                                aria-expanded="false" aria-controls="mobileSearch">
+                            <i class="bi bi-search"></i>
+                        </button>
+                    </div>
+
+                    <!-- Separator -->
+                    <div class="vr mx-2"></div>
+
                     <!-- Add host -->
                     <button
                             class="btn btn-primary d-flex align-items-center gap-2 px-3"
                     </button>
                 </div>
             </div>
+
+            <!-- Search collapsabile -->
+            <div class="collapse mt-2 d-md-none" id="mobileSearch">
+                <input
+                    type="text"
+                    id="searchInputMobile"
+                    placeholder="Search..."
+                    class="form-control form-control-sm placeholder-italic"
+                    aria-label="Search lease">
+            </div>
         </div>
     </section>
 
         <table id="dataTable" class="table table-bordered table-hover align-middle">
             <thead class="table-light">
                 <tr>
-                    <th data-type="ipv4"   data-sortable="true"  data-sort="0">IP Address<span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="mac"    data-sortable="true"  data-sort="1">MAC       <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="true"  data-sort="2">Hostname  <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="true"  data-sort="3">Start     <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="true"  data-sort="4">End       <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="true"  data-sort="5">State     <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="false" data-sort="6">Actions   <span class="sort-arrow" aria-hidden="true"></span></th>
+                    <th data-type="ipv4" data-sortable="true" data-sort="0">
+                        IP Address <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="mac" data-sortable="true" data-sort="1">
+                        MAC <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="true" data-sort="2">
+                        Hostname <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="true" data-sort="3">
+                        Start <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="true" data-sort="4">
+                        End <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="true" data-sort="5">
+                        State <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="false" class="text-center text-nowrap">
+                        Actions <span class="sort-arrow invisible" aria-hidden="true"></span>
+                    </th>
                 </tr>
             </thead>
             <tbody></tbody>
index 43cd2a184d731df1ca6456c85f461c001de17d68..98df7d2124485e6ab24a542f09c359b7e3483e51 100644 (file)
@@ -66,6 +66,7 @@
     <section class="page-frame">
         <div class="container-fluid p-0">
             <div class="row g-2 align-items-center">
+
                 <!-- Title -->
                 <div class="col-12 col-md-auto">
                     <h2 class="mb-0 d-flex align-items-center gap-2 lh-1">
                 <!-- Spacer -->
                 <div class="col d-none d-md-block"></div>
 
-                <!-- Search -->
-                <div class="col-12 col-md-auto">
+                <!-- Search Desktop -->
+                <div class="col-12 col-md-auto d-none d-md-block">
                     <div class="search-wrapper">
                         <input
                             type="text"
                             id="searchInput"
                             placeholder="Search..."
                             class="form-control form-control-sm placeholder-italic"
-                            aria-label="Search config"
-                        >
+                            aria-label="Search config">
                     </div>
                 </div>
 
                 <!-- Bottoni -->
                 <div class="col-12 col-md-auto d-flex align-items-center gap-2 flex-wrap">
 
-                       <!-- Expand -->
+                    <!-- Search Mobile -->
+                    <div class="col-auto d-md-none">
+                        <button
+                                class="btn btn-primary d-flex align-items-center gap-2 px-3"
+                                data-bs-toggle="collapse" data-bs-target="#mobileSearch"
+                                aria-expanded="false" aria-controls="mobileSearch">
+                            <i class="bi bi-search"></i>
+                        </button>
+                    </div>
+
+                    <!-- Separator -->
+                    <div class="vr mx-2"></div>
+
+                    <!-- Expand -->
                     <button id="expandAllBtn"
                             class="btn btn-sm btn-primary d-flex align-items-center gap-2 px-3"
                             title="Expand all groups"
                             aria-label="Expand all groups">
                         <i class="bi bi-arrows-expand"></i>
                         <span class="label">Expand</span>
-                       </button>
+                    </button>
 
                     <!-- Collapse -->
                     <button id="collapseAllBtn"
                     </button>
                 </div>
             </div>
+
+            <!-- Search collapsabile -->
+            <div class="collapse mt-2 d-md-none" id="mobileSearch">
+                <input
+                    type="text"
+                    id="searchInputMobile"
+                    placeholder="Search..."
+                    class="form-control form-control-sm placeholder-italic"
+                    aria-label="Search config">
+            </div>
         </div>
     </section>
 
         <table id="dataTable" class="table table-bordered table-hover align-middle">
             <thead class="table-light">
                 <tr>
-                    <th data-type="string" data-sortable="false">Parameter  <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="false">Value      <span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="false">Description<span class="sort-arrow" aria-hidden="true"></span></th>
-                    <th data-type="string" data-sortable="false">Actions    <span class="sort-arrow" aria-hidden="true"></span></th>
+                    <th data-type="string" data-sortable="false">
+                        Parameter <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="false">
+                        Value <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="false">
+                        Description <span class="sort-arrow" aria-hidden="true"></span>
+                    </th>
+
+                    <th data-type="string" data-sortable="false" class="text-center text-nowrap">
+                        Actions <span class="sort-arrow invisible" aria-hidden="true"></span>
+                    </th>
                 </tr>
             </thead>
             <tbody></tbody>