commit 02b8285: [WebUI] Add column display mode settings

moisseev moiseev at mezonplus.ru
Mon Jul 29 17:54:31 UTC 2024


Author: moisseev
Date: 2024-03-16 14:08:56 +0300
URL: https://github.com/rspamd/rspamd/commit/02b82858aa48ba25fb9db236e167c9c757e87e2b (refs/pull/4877/head)

[WebUI] Add column display mode settings
for Scan and History tables

---
 interface/index.html        |   9 ++++
 interface/js/app/history.js |   3 +-
 interface/js/app/libft.js   | 110 +++++++++++++++++++++++++++++++++++++++++++-
 interface/js/app/upload.js  |   5 +-
 4 files changed, 123 insertions(+), 4 deletions(-)

diff --git a/interface/index.html b/interface/index.html
index 27865c611..5b3187309 100644
--- a/interface/index.html
+++ b/interface/index.html
@@ -537,6 +537,10 @@
 								</select>
 							<label for="scan_page_size" class="ms-2">Rows per page:</label>
 							<input id="scan_page_size" class="form-control ms-1" value="25" min="1" type="number">
+							<button class="btn btn-outline-secondary btn-sm ms-2 d-flex align-items-center dropdown-toggle ft-columns-btn" type="button" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false" disabled>
+								<i class="fas fa-columns me-1"></i>Columns
+							</button>
+							<div class="dropdown-menu ft-columns-dropdown p-2"></div>
 							<button class="btn btn-secondary btn-sm ms-2 d-flex align-items-center" id="cleanScanHistory" disabled>
 								<i class="fas fa-trash-alt me-1"></i>Clean history
 							</button>
@@ -649,6 +653,11 @@
 							</select>
 							<label for="history_page_size" class="ms-2">Rows per page:</label>
 							<input id="history_page_size" class="form-control ms-1" value="25" min="1" type="number">
+							<button class="btn btn-outline-secondary btn-sm ms-2 d-flex align-items-center dropdown-toggle ft-columns-btn" type="button" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false" disabled>
+								<i class="fas fa-columns me-1"></i>Columns
+							</button>
+							<div class="dropdown-menu ft-columns-dropdown p-2"></div>
+
 							<button class="btn btn-danger btn-sm ms-2 d-flex align-items-center ro-hide" id="resetHistory">
 								<i class="fas fa-times-circle me-1"></i>Reset
 							</button>
diff --git a/interface/js/app/history.js b/interface/js/app/history.js
index a429b21ca..e486c1060 100644
--- a/interface/js/app/history.js
+++ b/interface/js/app/history.js
@@ -192,7 +192,8 @@ define(["jquery", "app/common", "app/libft", "footable"],
                             // Is there a way to get an event when the table is destroyed?
                             setTimeout(() => {
                                 libft.initHistoryTable(data, items, "history", get_history_columns(data), false,
-                                    () => $("#refresh, #updateHistory").removeAttr("disabled"));
+                                    () => $("#refresh, #updateHistory, #history .ft-columns-dropdown .btn-dropdown-apply")
+                                        .removeAttr("disabled"));
                             }, 200);
                         }
                         prevVersion = version;
diff --git a/interface/js/app/libft.js b/interface/js/app/libft.js
index faf16489b..2b2428385 100644
--- a/interface/js/app/libft.js
+++ b/interface/js/app/libft.js
@@ -4,6 +4,7 @@ define(["jquery", "app/common", "footable"],
     ($, common) => {
         "use strict";
         const ui = {};
+        const columnsCustom = JSON.parse(localStorage.getItem("columns")) || {};
 
         let pageSizeTimerId = null;
         let pageSizeInvocationCounter = 0;
@@ -230,13 +231,15 @@ define(["jquery", "app/common", "footable"],
         };
 
         ui.destroyTable = function (table) {
+            $("#" + table + " .ft-columns-btn.show").trigger("click.bs.dropdown"); // Hide dropdown
+            $("#" + table + " .ft-columns-btn").attr("disabled", true);
             if (common.tables[table]) {
                 common.tables[table].destroy();
                 delete common.tables[table];
             }
         };
 
-        ui.initHistoryTable = function (data, items, table, columns, expandFirst, postdrawCallback) {
+        ui.initHistoryTable = function (data, items, table, columnsDefault, expandFirst, postdrawCallback) {
             /* eslint-disable no-underscore-dangle */
             FooTable.Cell.extend("collapse", function () {
                 // call the original method
@@ -318,6 +321,10 @@ define(["jquery", "app/common", "footable"],
             });
             /* eslint-enable consistent-this, no-underscore-dangle, one-var-declaration-per-line */
 
+            const columns = (table in columnsCustom)
+                ? columnsDefault.map((column) => $.extend({}, column, columnsCustom[table][column.name]))
+                : columnsDefault.map((column) => column);
+
             common.tables[table] = FooTable.init("#historyTable_" + table, {
                 columns: columns,
                 rows: items,
@@ -350,6 +357,107 @@ define(["jquery", "app/common", "footable"],
                     "postdraw.ft.table": postdrawCallback
                 }
             });
+
+            // Column options dropdown
+            (() => {
+                function updateValue(checked, column, cellIdx) {
+                    const option = ["breakpoints", "visible"][cellIdx];
+                    const value = [(checked) ? "all" : column.breakpoints, !checked][cellIdx];
+
+                    FooTable.get("#historyTable_" + table).columns.get(column.name)[option] = value;
+                    return value;
+                }
+
+                const tbody = $("<tbody/>", {class: "table-group-divider"});
+                $("#" + table + " .ft-columns-dropdown").empty().append(
+                    $("<table/>", {class: "table table-sm table-striped text-center"}).append(
+                        $("<thead/>").append(
+                            $("<tr/>").append(
+                                $("<th/>", {text: "Row", title: "Display column cells in a detail row on all screen widths"}),
+                                $("<th/>", {text: "Hidden", title: "Hide column completely"}),
+                                $("<th/>", {text: "Column name", class: "text-start"})
+                            )
+                        ),
+                        tbody
+                    ),
+                    $("<button/>", {
+                        type: "button",
+                        class: "btn btn-xs btn-secondary float-start",
+                        text: "Reset to default",
+                        click: () => {
+                            columnsDefault.forEach((column, i) => {
+                                const row = tbody[0].rows[i];
+                                [(column.breakpoints === "all"), (column.visible === false)].forEach((checked, cellIdx) => {
+                                    if (row.cells[cellIdx].getElementsByTagName("input")[0].checked !== checked) {
+                                        row.cells[cellIdx].getElementsByTagName("input")[0].checked = checked;
+
+                                        updateValue(checked, column, cellIdx);
+                                        delete columnsCustom[table];
+                                    }
+                                });
+                            });
+                        }
+                    }),
+                    $("<button/>", {
+                        type: "button",
+                        class: "btn btn-xs btn-primary float-end btn-dropdown-apply",
+                        text: "Apply",
+                        title: "Save settings and redraw the table",
+                        click: (e) => {
+                            $(e.target).attr("disabled", true);
+                            FooTable.get("#historyTable_" + table).draw();
+                            localStorage.setItem("columns", JSON.stringify(columnsCustom));
+                        }
+                    })
+                );
+
+                function checkbox(i, column, cellIdx) {
+                    const option = ["breakpoints", "visible"][cellIdx];
+                    return $("<td/>").append($("<input/>", {
+                        "type": "checkbox",
+                        "class": "form-check-input",
+                        "data-table": table,
+                        "data-name": column.name,
+                        "checked": (option === "breakpoints" && column.breakpoints === "all") ||
+                            (option === "visible" && column.visible === false),
+                        "disabled": (option === "breakpoints" && columnsDefault[i].breakpoints === "all")
+                    }).change((e) => {
+                        const value = updateValue(e.target.checked, columnsDefault[i], cellIdx);
+                        if (value == null) { // eslint-disable-line no-eq-null, eqeqeq
+                            delete columnsCustom[table][column.name][option];
+                        } else {
+                            $.extend(true, columnsCustom, {
+                                [table]: {
+                                    [column.name]: {
+                                        [option]: value
+                                    }
+                                }
+                            });
+                        }
+                    }));
+                }
+
+                $.each(columns, (i, column) => {
+                    tbody.append(
+                        $("<tr/>").append(
+                            checkbox(i, column, 0),
+                            checkbox(i, column, 1),
+                            $("<td/>", {
+                                class: "text-start",
+                                text: () => {
+                                    switch (column.name) {
+                                        case "passthrough_module": return "Pass-through module";
+                                        case "symbols": return "Symbols";
+                                        default: return column.title;
+                                    }
+                                }
+                            })
+                        )
+                    );
+                });
+
+                $("#" + table + " .ft-columns-btn").removeAttr("disabled");
+            })();
         };
 
         ui.preprocess_item = function (item) {
diff --git a/interface/js/app/upload.js b/interface/js/app/upload.js
index c464ef30f..5f330002b 100644
--- a/interface/js/app/upload.js
+++ b/interface/js/app/upload.js
@@ -76,7 +76,7 @@ define(["jquery", "app/common", "app/libft"],
         }
 
         function enable_disable_scan_btn(disable) {
-            $("#scan button:not(#cleanScanHistory, #scanOptionsToggle)")
+            $("#scan button:not(#cleanScanHistory, #scanOptionsToggle, .ft-columns-btn)")
                 .prop("disabled", (disable || $.trim($("textarea").val()).length === 0));
         }
 
@@ -128,7 +128,8 @@ define(["jquery", "app/common", "app/libft"],
                                             }, ++filesIdx);
                                         } else {
                                             enable_disable_scan_btn();
-                                            $("#cleanScanHistory").removeAttr("disabled");
+                                            $("#cleanScanHistory, #scan .ft-columns-dropdown .btn-dropdown-apply")
+                                                .removeAttr("disabled");
                                             $("html, body").animate({
                                                 scrollTop: $("#scanResult").offset().top
                                             }, 1000);


More information about the Commits mailing list