diff options
-rw-r--r-- | interface/css/rspamd.css | 4 | ||||
-rw-r--r-- | interface/index.html | 18 | ||||
-rw-r--r-- | interface/js/app/history.js | 57 | ||||
-rw-r--r-- | interface/js/app/rspamd.js | 21 |
4 files changed, 93 insertions, 7 deletions
diff --git a/interface/css/rspamd.css b/interface/css/rspamd.css index 9f97a668b..54310049b 100644 --- a/interface/css/rspamd.css +++ b/interface/css/rspamd.css @@ -420,8 +420,10 @@ table#symbolsTable input[type="number"] { display: none; } +#history-from, +#history-count, #history_page_size { - width: 6em !important; + width: 6em; text-align: center; } diff --git a/interface/index.html b/interface/index.html index 30181e788..165eae200 100644 --- a/interface/index.html +++ b/interface/index.html @@ -124,6 +124,16 @@ </div> </div> </div> + + <div class="card mt-1"> + <div class="card-body"> + <h6 class="card-title fw-bolder">History rows per load</h6> + <div class="input-group input-group-sm was-validated"> + <input type="number" id="settings-history-count" class="form-control" min="1" step="1" placeholder="1000"> + <button id="settings-history-count-restore" class="btn btn-secondary">Restore default</button> + </div> + </div> + </div> </div> </div> </form> @@ -681,9 +691,13 @@ <option value="score">Score value</option> <option value="name">Name</option> </select> - <label for="history_page_size" class="ms-2">Rows per page:</label> + <label for="history-from" class="ms-3" title="Start from this row number">Offset:</label> + <input type="number" id="history-from" class="form-control ms-1" value="0" min="0" step="1" title="Start from this row number"> + <label for="history-count" class="ms-2" title="Number of rows to load">Count:</label> + <input type="number" id="history-count" class="form-control ms-1" value="1000" min="1" step="1" title="Number of rows to load"> + <label for="history_page_size" class="ms-2">Rows/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> + <button class="btn btn-outline-secondary btn-sm ms-3 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> diff --git a/interface/js/app/history.js b/interface/js/app/history.js index 185922087..bf1dbae53 100644 --- a/interface/js/app/history.js +++ b/interface/js/app/history.js @@ -30,6 +30,12 @@ define(["jquery", "app/common", "app/libft", "footable"], const ui = {}; let prevVersion = null; + // History range: offset and count + const histFromDef = 0; + const historyCountDef = 1000; + let histFrom = histFromDef; + let histCount = parseInt(localStorage.getItem("historyCount"), 10) || historyCountDef; + function process_history_legacy(data) { const items = []; @@ -152,7 +158,8 @@ define(["jquery", "app/common", "app/libft", "footable"], ui.getHistory = function () { $("#refresh, #updateHistory").attr("disabled", true); - common.query("history", { + const histTo = histFrom - 1 + histCount; + common.query(`history?from=${histFrom}&to=${histTo}`, { success: function (req_data) { function differentVersions(neighbours_data) { const dv = neighbours_data.some((e) => e.version !== neighbours_data[0].version); @@ -192,8 +199,10 @@ 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, #history .ft-columns-dropdown .btn-dropdown-apply") - .removeAttr("disabled")); + () => { + $("#history .ft-columns-dropdown .btn-dropdown-apply").removeAttr("disabled"); + ui.updateHistoryControlsState(); + }); }, 200); } prevVersion = version; @@ -201,7 +210,7 @@ define(["jquery", "app/common", "app/libft", "footable"], libft.destroyTable("history"); } }, - error: () => $("#refresh, #updateHistory").removeAttr("disabled"), + error: () => ui.updateHistoryControlsState(), errorMessage: "Cannot receive history", }); }; @@ -282,6 +291,46 @@ define(["jquery", "app/common", "app/libft", "footable"], }); }; + ui.updateHistoryControlsState = function () { + const from = parseInt($("#history-from").val(), 10); + const count = parseInt($("#history-count").val(), 10); + const valid = !(isNaN(from) || from < 0 || isNaN(count) || count < 1); + + if (valid) { + $("#refresh, #updateHistory").removeAttr("disabled").removeClass("disabled"); + } else { + $("#refresh, #updateHistory").attr("disabled", true).addClass("disabled"); + } + }; + + function validateAndClampInput(el) { + const min = el.id === "history-from" ? 0 : 1; + let v = parseInt(el.value, 10); + if (isNaN(v) || v < min) { + v = min; + $(el).addClass("is-invalid"); + } else { + $(el).removeClass("is-invalid"); + } + return v; + } + + $("#history-from").val(histFrom); + $("#history-count").val(histCount); + $("#history-from, #history-count").on("input", (e) => { + validateAndClampInput(e.currentTarget); + ui.updateHistoryControlsState(); + }); + $("#history-from, #history-count").on("blur", (e) => { + const el = e.currentTarget; + const v = validateAndClampInput(el); + $(el).val(v).removeClass("is-invalid"); + ui.updateHistoryControlsState(); + }); + $("#history-from,#history-count").on("change", () => { + histFrom = parseInt($("#history-from").val(), 10) || histFromDef; + histCount = parseInt($("#history-count").val(), 10) || historyCountDef; + }); libft.set_page_size("history", $("#history_page_size").val()); libft.bindHistoryTableEventHandlers("history", 8); diff --git a/interface/js/app/rspamd.js b/interface/js/app/rspamd.js index cb7fb8ace..4b154c2ae 100644 --- a/interface/js/app/rspamd.js +++ b/interface/js/app/rspamd.js @@ -198,6 +198,8 @@ define(["jquery", "app/common", "stickytabs", "visibility", $(".preset").hide(); $(".history").show(); $(".dynamic").hide(); + + module.updateHistoryControlsState(); }); break; case "#disconnect": @@ -348,6 +350,8 @@ define(["jquery", "app/common", "stickytabs", "visibility", let selected_locale = null; let custom_locale = null; const localeTextbox = ".popover #settings-popover #locale"; + const historyCountDef = 1000; + const historyCountSelector = ".popover #settings-popover #settings-history-count"; function validateLocale(saveToLocalStorage) { function toggle_form_group_class(remove, add) { @@ -406,6 +410,8 @@ define(["jquery", "app/common", "stickytabs", "visibility", $(localeTextbox).val(custom_locale); ajaxSetup(localStorage.getItem("ajax_timeout"), true); + + $(historyCountSelector).val(parseInt(localStorage.getItem("historyCount"), 10) || historyCountDef); }); $(document).on("change", '.popover #settings-popover input:radio[name="locale"]', function () { selected_locale = this.value; @@ -423,6 +429,21 @@ define(["jquery", "app/common", "stickytabs", "visibility", ajaxSetup(null, true, true); }); + $(document).on("input", historyCountSelector, (e) => { + const v = parseInt($(e.currentTarget).val(), 10); + if (v > 0) { + localStorage.setItem("historyCount", v); + $(e.currentTarget).removeClass("is-invalid"); + $("#history-count").val(v).trigger("change"); + } else { + $(e.currentTarget).addClass("is-invalid"); + } + }); + $(document).on("click", ".popover #settings-popover #settings-history-count-restore", () => { + localStorage.removeItem("historyCount"); + $(historyCountSelector).val(historyCountDef); + }); + // Dismiss Bootstrap popover by clicking outside $("body").on("click", (e) => { $(".popover").each(function () { |