mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
refactor: move debounce function to common.js and clean up usage across scripts
This commit is contained in:
parent
cb62f550ec
commit
3b8ab7c964
17 changed files with 1250 additions and 2151 deletions
17
src/ui/app/static/js/common.js
Normal file
17
src/ui/app/static/js/common.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// common.js
|
||||
|
||||
/**
|
||||
* Debounce function to limit the rate at which a function can fire.
|
||||
* @param {Function} func - The function to debounce.
|
||||
* @param {number} delay - The delay in milliseconds.
|
||||
* @returns {Function} Debounced function.
|
||||
*/
|
||||
function debounce(func, delay) {
|
||||
let timer = null;
|
||||
return (...args) => {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
func(...args);
|
||||
}, delay);
|
||||
};
|
||||
}
|
||||
183
src/ui/app/static/js/dataTableInit.js
Normal file
183
src/ui/app/static/js/dataTableInit.js
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
// dataTableInit.js
|
||||
|
||||
function initializeDataTable(config) {
|
||||
const {
|
||||
tableSelector,
|
||||
tableName,
|
||||
columnVisibilityCondition,
|
||||
dataTableOptions,
|
||||
} = config;
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
const searchPanesContainer = dataTable.searchPanes.container();
|
||||
if (!searchPanesContainer) return;
|
||||
dataTable.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
// Initialize DataTable
|
||||
const dataTable = new DataTable(tableSelector, dataTableOptions);
|
||||
|
||||
if (dataTable.searchPanes.container())
|
||||
dataTable.searchPanes.container().hide();
|
||||
|
||||
$(".dt-type-numeric").removeClass("dt-type-numeric");
|
||||
|
||||
$(".action-button")
|
||||
.parent()
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
|
||||
$(tableSelector).removeClass("d-none");
|
||||
$(`#${tableName}-waiting`).addClass("visually-hidden");
|
||||
|
||||
const $columnsPreferenceDefaults = $("#columns_preferences_defaults");
|
||||
const $columnsPreference = $("#columns_preferences");
|
||||
|
||||
if ($columnsPreferenceDefaults.length && $columnsPreference.length) {
|
||||
const defaultColsVisibility = JSON.parse(
|
||||
$columnsPreferenceDefaults.val().trim(),
|
||||
);
|
||||
|
||||
// Handle column visibility preferences
|
||||
let columnVisibility = localStorage.getItem(`bw-${tableName}-columns`);
|
||||
if (columnVisibility === null) {
|
||||
columnVisibility = JSON.parse($columnsPreference.val().trim());
|
||||
} else {
|
||||
columnVisibility = JSON.parse(columnVisibility);
|
||||
}
|
||||
|
||||
Object.entries(columnVisibility).forEach(([key, value]) => {
|
||||
dataTable.column(key).visible(value);
|
||||
});
|
||||
|
||||
// Save column preferences
|
||||
const saveColumnsPreferences = debounce(() => {
|
||||
const data = new FormData();
|
||||
data.append("csrf_token", $("#csrf_token").val().trim());
|
||||
data.append("table_name", tableName);
|
||||
data.append("columns_preferences", JSON.stringify(columnVisibility));
|
||||
|
||||
const savePreferencesUrl = $("#home-path")
|
||||
.val()
|
||||
.trim()
|
||||
.replace(/\/home$/, "/set_columns_preferences");
|
||||
|
||||
fetch(savePreferencesUrl, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("There was a problem with the fetch operation:", error);
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
// Column visibility event
|
||||
dataTable.on("column-visibility.dt", function (e, settings, column, state) {
|
||||
if (
|
||||
typeof columnVisibilityCondition === "function" &&
|
||||
!columnVisibilityCondition(column)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
columnVisibility[column] = state;
|
||||
const isDefault =
|
||||
JSON.stringify(columnVisibility) ===
|
||||
JSON.stringify(defaultColsVisibility);
|
||||
|
||||
if (isDefault) {
|
||||
localStorage.removeItem(`bw-${tableName}-columns`);
|
||||
} else {
|
||||
localStorage.setItem(
|
||||
`bw-${tableName}-columns`,
|
||||
JSON.stringify(columnVisibility),
|
||||
);
|
||||
}
|
||||
|
||||
saveColumnsPreferences();
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on("hidden.bs.toast", ".toast", function (event) {
|
||||
if (event.target.id.startsWith("feedback-toast")) {
|
||||
setTimeout(() => {
|
||||
$(this).remove();
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
if (dataTable.responsive) dataTable.responsive.recalc();
|
||||
|
||||
dataTable.on("mouseenter", "td", function () {
|
||||
if (dataTable.cell(this).index() === undefined) return;
|
||||
const rowIdx = dataTable.cell(this).index().row;
|
||||
|
||||
dataTable
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
|
||||
dataTable
|
||||
.cells()
|
||||
.nodes()
|
||||
.each(function (el) {
|
||||
if (dataTable.cell(el).index().row === rowIdx)
|
||||
el.classList.add("highlight");
|
||||
});
|
||||
});
|
||||
|
||||
dataTable.on("mouseleave", "td", function () {
|
||||
dataTable
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
dataTable.on("select", function (e, dt, type, indexes) {
|
||||
const actionButton = $(".action-button");
|
||||
if (!actionButton.length) return;
|
||||
|
||||
// Enable the actions button
|
||||
actionButton
|
||||
.removeClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", null)
|
||||
.attr("data-bs-original-title", null)
|
||||
.attr("data-bs-placement", null)
|
||||
.tooltip("dispose");
|
||||
});
|
||||
|
||||
dataTable.on("deselect", function (e, dt, type, indexes) {
|
||||
// If no rows are selected, disable the actions button
|
||||
if (dataTable.rows({ selected: true }).count() === 0) {
|
||||
const actionButton = $(".action-button");
|
||||
if (!actionButton.length) return;
|
||||
|
||||
actionButton
|
||||
.addClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", "tooltip")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
}
|
||||
});
|
||||
|
||||
return dataTable;
|
||||
}
|
||||
|
|
@ -136,20 +136,6 @@ $(document).ready(function () {
|
|||
$("#selected-ips-input-unban").val(JSON.stringify(bans));
|
||||
};
|
||||
|
||||
const debounce = (func, delay) => {
|
||||
let timer = null;
|
||||
|
||||
return (...args) => {
|
||||
// Clear the timer if the function is called again during the delay
|
||||
if (timer) clearTimeout(timer);
|
||||
|
||||
// Start a new timer to invoke the function after the delay
|
||||
timer = setTimeout(() => {
|
||||
func(...args);
|
||||
}, delay);
|
||||
};
|
||||
};
|
||||
|
||||
const layout = {
|
||||
top1: {
|
||||
searchPanes: {
|
||||
|
|
@ -295,15 +281,6 @@ $(document).ready(function () {
|
|||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
bans_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.unban_ips = {
|
||||
text: '<span class="tf-icons bx bxs-buoy bx-18px me-2"></span>Unban',
|
||||
action: function (e, dt, node, config) {
|
||||
|
|
@ -329,286 +306,163 @@ $(document).ready(function () {
|
|||
},
|
||||
};
|
||||
|
||||
const bans_table = new DataTable("#bans", {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
render: DataTable.render.select(),
|
||||
targets: 1,
|
||||
},
|
||||
{ type: "ip-address", targets: 2 },
|
||||
{
|
||||
orderable: false,
|
||||
targets: -1,
|
||||
},
|
||||
{
|
||||
targets: [2, 6],
|
||||
render: function (data, type, row) {
|
||||
if (type === "display" || type === "filter") {
|
||||
const date = new Date(data);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date.toLocaleString();
|
||||
initializeDataTable({
|
||||
tableSelector: "#bans",
|
||||
tableName: "bans",
|
||||
columnVisibilityCondition: (column) => column > 2 && column < 8,
|
||||
dataTableOptions: {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
render: DataTable.render.select(),
|
||||
targets: 1,
|
||||
},
|
||||
{ type: "ip-address", targets: 2 },
|
||||
{
|
||||
orderable: false,
|
||||
targets: -1,
|
||||
},
|
||||
{
|
||||
targets: [2, 6],
|
||||
render: function (data, type, row) {
|
||||
if (type === "display" || type === "filter") {
|
||||
const date = new Date(data);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date.toLocaleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
return data;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Last 24 hours",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[2]);
|
||||
const now = new Date();
|
||||
return now - date < 24 * 60 * 60 * 1000;
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Last 24 hours",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[2]);
|
||||
const now = new Date();
|
||||
return now - date < 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Last 7 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[2]);
|
||||
const now = new Date();
|
||||
return now - date < 7 * 24 * 60 * 60 * 1000;
|
||||
{
|
||||
label: "Last 7 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[2]);
|
||||
const now = new Date();
|
||||
return now - date < 7 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Last 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[2]);
|
||||
const now = new Date();
|
||||
return now - date < 30 * 24 * 60 * 60 * 1000;
|
||||
{
|
||||
label: "Last 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[2]);
|
||||
const now = new Date();
|
||||
return now - date < 30 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "More than 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[2]);
|
||||
const now = new Date();
|
||||
return now - date >= 30 * 24 * 60 * 60 * 1000;
|
||||
{
|
||||
label: "More than 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[2]);
|
||||
const now = new Date();
|
||||
return now - date >= 30 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 2,
|
||||
},
|
||||
targets: 2,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Next 24 hours",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[6]);
|
||||
const now = new Date();
|
||||
return date - now < 24 * 60 * 60 * 1000;
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Next 24 hours",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[6]);
|
||||
const now = new Date();
|
||||
return date - now < 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Next 7 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[6]);
|
||||
const now = new Date();
|
||||
return date - now < 7 * 24 * 60 * 60 * 1000;
|
||||
{
|
||||
label: "Next 7 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[6]);
|
||||
const now = new Date();
|
||||
return date - now < 7 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Next 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[6]);
|
||||
const now = new Date();
|
||||
return date - now < 30 * 24 * 60 * 60 * 1000;
|
||||
{
|
||||
label: "Next 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[6]);
|
||||
const now = new Date();
|
||||
return date - now < 30 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "More than 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[6]);
|
||||
const now = new Date();
|
||||
return date - now >= 30 * 24 * 60 * 60 * 1000;
|
||||
{
|
||||
label: "More than 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[6]);
|
||||
const now = new Date();
|
||||
return date - now >= 30 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 6,
|
||||
},
|
||||
targets: 6,
|
||||
},
|
||||
{
|
||||
searchPanes: { show: true },
|
||||
targets: 5,
|
||||
},
|
||||
],
|
||||
order: [[6, "asc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
select: {
|
||||
style: "multi+shift",
|
||||
selector: "td:nth-child(2)",
|
||||
headerCheckbox: true,
|
||||
},
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ bans",
|
||||
infoEmpty: "No bans available",
|
||||
infoFiltered: "(filtered from _MAX_ total bans)",
|
||||
lengthMenu: "Display _MENU_ bans",
|
||||
zeroRecords: "No matching bans found",
|
||||
{
|
||||
searchPanes: { show: true },
|
||||
targets: 5,
|
||||
},
|
||||
],
|
||||
order: [[6, "asc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
select: {
|
||||
rows: {
|
||||
_: "Selected %d bans",
|
||||
0: "No bans selected",
|
||||
1: "Selected 1 ban",
|
||||
style: "multi+shift",
|
||||
selector: "td:nth-child(2)",
|
||||
headerCheckbox: true,
|
||||
},
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ bans",
|
||||
infoEmpty: "No bans available",
|
||||
infoFiltered: "(filtered from _MAX_ total bans)",
|
||||
lengthMenu: "Display _MENU_ bans",
|
||||
zeroRecords: "No matching bans found",
|
||||
select: {
|
||||
rows: {
|
||||
_: "Selected %d bans",
|
||||
0: "No bans selected",
|
||||
1: "Selected 1 ban",
|
||||
},
|
||||
},
|
||||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#bans_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
if (isReadOnly)
|
||||
$("#bans_wrapper .dt-buttons")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"The database is in readonly, therefore you cannot add bans.",
|
||||
)
|
||||
.attr("data-bs-placement", "right")
|
||||
.tooltip();
|
||||
},
|
||||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#bans_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
if (isReadOnly)
|
||||
$("#bans_wrapper .dt-buttons")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"The database is in readonly, therefore you cannot add bans.",
|
||||
)
|
||||
.attr("data-bs-placement", "right")
|
||||
.tooltip();
|
||||
},
|
||||
});
|
||||
|
||||
bans_table.searchPanes.container().hide();
|
||||
|
||||
$(".action-button")
|
||||
.parent()
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
|
||||
$("#bans").removeClass("d-none");
|
||||
$("#bans-waiting").addClass("visually-hidden");
|
||||
|
||||
const defaultColsVisibility = JSON.parse(
|
||||
$("#columns_preferences_defaults").val().trim(),
|
||||
);
|
||||
|
||||
var columnVisibility = localStorage.getItem("bw-bans-columns");
|
||||
if (columnVisibility === null) {
|
||||
columnVisibility = JSON.parse($("#columns_preferences").val().trim());
|
||||
} else {
|
||||
columnVisibility = JSON.parse(columnVisibility);
|
||||
}
|
||||
|
||||
Object.entries(columnVisibility).forEach(([key, value]) => {
|
||||
bans_table.column(key).visible(value);
|
||||
});
|
||||
|
||||
bans_table.responsive.recalc();
|
||||
|
||||
bans_table.on("mouseenter", "td", function () {
|
||||
if (bans_table.cell(this).index() === undefined) return;
|
||||
const rowIdx = bans_table.cell(this).index().row;
|
||||
|
||||
bans_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
|
||||
bans_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each(function (el) {
|
||||
if (bans_table.cell(el).index().row === rowIdx)
|
||||
el.classList.add("highlight");
|
||||
});
|
||||
});
|
||||
|
||||
bans_table.on("mouseleave", "td", function () {
|
||||
bans_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
bans_table.on("select", function (e, dt, type, indexes) {
|
||||
// Enable the actions button
|
||||
$(".action-button")
|
||||
.removeClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", null)
|
||||
.attr("data-bs-original-title", null)
|
||||
.attr("data-bs-placement", null)
|
||||
.tooltip("dispose");
|
||||
});
|
||||
|
||||
bans_table.on("deselect", function (e, dt, type, indexes) {
|
||||
// If no rows are selected, disable the actions button
|
||||
if (bans_table.rows({ selected: true }).count() === 0) {
|
||||
$(".action-button")
|
||||
.addClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", "tooltip")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
}
|
||||
});
|
||||
|
||||
const saveColumnsPreferences = debounce(() => {
|
||||
const rootUrl = $("#home-path")
|
||||
.val()
|
||||
.trim()
|
||||
.replace(/\/home$/, "/set_columns_preferences");
|
||||
const csrfToken = $("#csrf_token").val();
|
||||
|
||||
const data = new FormData();
|
||||
data.append("csrf_token", csrfToken);
|
||||
data.append("table_name", "bans");
|
||||
data.append("columns_preferences", JSON.stringify(columnVisibility));
|
||||
|
||||
fetch(rootUrl, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
console.log("Preferences saved successfully!");
|
||||
// Handle success, redirect, etc.
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("There was a problem with the fetch operation:", error);
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
bans_table.on("column-visibility.dt", function (e, settings, column, state) {
|
||||
if (column < 3 || column === 8) return;
|
||||
columnVisibility[column] = state;
|
||||
// Check if columVisibility is equal to defaultColsVisibility
|
||||
const isDefault =
|
||||
JSON.stringify(columnVisibility) ===
|
||||
JSON.stringify(defaultColsVisibility);
|
||||
// If it is, remove the key from localStorage
|
||||
if (isDefault) {
|
||||
localStorage.removeItem("bw-bans-columns");
|
||||
} else {
|
||||
localStorage.setItem("bw-bans-columns", JSON.stringify(columnVisibility));
|
||||
}
|
||||
|
||||
saveColumnsPreferences();
|
||||
});
|
||||
|
||||
$(document).on("click", ".unban-ip", function () {
|
||||
|
|
|
|||
|
|
@ -123,113 +123,101 @@ $(document).ready(function () {
|
|||
},
|
||||
];
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: `<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters"${
|
||||
cacheJobNameSelection || cachePluginSelection || cacheServiceSelection
|
||||
? ' class="d-none"'
|
||||
: ""
|
||||
}>Show</span><span id="hide-filters"${
|
||||
!cacheJobNameSelection && !cachePluginSelection && !cacheServiceSelection
|
||||
? ' class="d-none"'
|
||||
: ""
|
||||
}>Hide</span><span class="d-none d-md-inline"> filters</span>`,
|
||||
action: function (e, dt, node, config) {
|
||||
cache_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
const cache_table = new DataTable("#cache", {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
targets: -1,
|
||||
},
|
||||
{
|
||||
visible: false,
|
||||
targets: 6,
|
||||
},
|
||||
{
|
||||
targets: 5,
|
||||
render: function (data, type, row) {
|
||||
if (type === "display" || type === "filter") {
|
||||
const date = new Date(data);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date.toLocaleString();
|
||||
}
|
||||
}
|
||||
return data;
|
||||
},
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
},
|
||||
targets: [2, 3],
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
options: servicesSearchPanesOptions,
|
||||
},
|
||||
targets: 4,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Last 24 hours",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[5]);
|
||||
const now = new Date();
|
||||
return now - date < 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Last 7 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[5]);
|
||||
const now = new Date();
|
||||
return now - date < 7 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Last 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[5]);
|
||||
const now = new Date();
|
||||
return now - date < 30 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
const cache_table = initializeDataTable({
|
||||
tableSelector: "#cache",
|
||||
tableName: "cache",
|
||||
columnVisibilityCondition: (column) => 2 < column && column < 7,
|
||||
dataTableOptions: {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
targets: 5,
|
||||
{
|
||||
orderable: false,
|
||||
targets: -1,
|
||||
},
|
||||
{
|
||||
visible: false,
|
||||
targets: 6,
|
||||
},
|
||||
{
|
||||
targets: 5,
|
||||
render: function (data, type, row) {
|
||||
if (type === "display" || type === "filter") {
|
||||
const date = new Date(data);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date.toLocaleString();
|
||||
}
|
||||
}
|
||||
return data;
|
||||
},
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
},
|
||||
targets: [2, 3],
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
options: servicesSearchPanesOptions,
|
||||
},
|
||||
targets: 4,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Last 24 hours",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[5]);
|
||||
const now = new Date();
|
||||
return now - date < 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Last 7 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[5]);
|
||||
const now = new Date();
|
||||
return now - date < 7 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Last 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[5]);
|
||||
const now = new Date();
|
||||
return now - date < 30 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 5,
|
||||
},
|
||||
],
|
||||
order: [[3, "asc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ cache files",
|
||||
infoEmpty: "No cache files available",
|
||||
infoFiltered: "(filtered from _MAX_ total cache files)",
|
||||
lengthMenu: "Display _MENU_ cache files",
|
||||
zeroRecords: "No matching cache files found",
|
||||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#cache_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
},
|
||||
],
|
||||
order: [[3, "asc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ cache files",
|
||||
infoEmpty: "No cache files available",
|
||||
infoFiltered: "(filtered from _MAX_ total cache files)",
|
||||
lengthMenu: "Display _MENU_ cache files",
|
||||
zeroRecords: "No matching cache files found",
|
||||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#cache_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -245,71 +233,9 @@ $(document).ready(function () {
|
|||
"click",
|
||||
);
|
||||
|
||||
if (!cacheJobNameSelection && !cachePluginSelection && !cacheServiceSelection)
|
||||
cache_table.searchPanes.container().hide();
|
||||
|
||||
$("#cache").removeClass("d-none");
|
||||
$("#cache-waiting").addClass("visually-hidden");
|
||||
|
||||
const defaultColsVisibility = {
|
||||
3: true,
|
||||
4: true,
|
||||
5: true,
|
||||
6: false,
|
||||
};
|
||||
|
||||
var columnVisibility = localStorage.getItem("bw-cache-columns");
|
||||
if (columnVisibility === null) {
|
||||
columnVisibility = JSON.parse(JSON.stringify(defaultColsVisibility));
|
||||
} else {
|
||||
columnVisibility = JSON.parse(columnVisibility);
|
||||
Object.entries(columnVisibility).forEach(([key, value]) => {
|
||||
cache_table.column(key).visible(value);
|
||||
});
|
||||
if (cacheJobNameSelection || cachePluginSelection || cacheServiceSelection) {
|
||||
cache_table.searchPanes.container().show();
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
}
|
||||
|
||||
cache_table.responsive.recalc();
|
||||
|
||||
cache_table.on("mouseenter", "td", function () {
|
||||
if (cache_table.cell(this).index() === undefined) return;
|
||||
const rowIdx = cache_table.cell(this).index().row;
|
||||
|
||||
cache_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
|
||||
cache_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each(function (el) {
|
||||
if (cache_table.cell(el).index().row === rowIdx)
|
||||
el.classList.add("highlight");
|
||||
});
|
||||
});
|
||||
|
||||
cache_table.on("mouseleave", "td", function () {
|
||||
cache_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
cache_table.on("column-visibility.dt", function (e, settings, column, state) {
|
||||
if (column < 3 || column === 7) return;
|
||||
columnVisibility[column] = state;
|
||||
// Check if columVisibility is equal to defaultColsVisibility
|
||||
const isDefault =
|
||||
JSON.stringify(columnVisibility) ===
|
||||
JSON.stringify(defaultColsVisibility);
|
||||
// If it is, remove the key from localStorage
|
||||
if (isDefault) {
|
||||
localStorage.removeItem("bw-cache-columns");
|
||||
} else {
|
||||
localStorage.setItem(
|
||||
"bw-cache-columns",
|
||||
JSON.stringify(columnVisibility),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -65,14 +65,6 @@ $(document).ready(function () {
|
|||
const $serviceDropdownItems = $("#services-dropdown-menu li.nav-item");
|
||||
const $typeDropdownItems = $("#types-dropdown-menu li.nav-item");
|
||||
|
||||
const debounce = (func, delay) => {
|
||||
let debounceTimer;
|
||||
return (...args) => {
|
||||
clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(() => func.apply(this, args), delay);
|
||||
};
|
||||
};
|
||||
|
||||
const changeTypesVisibility = () => {
|
||||
$typeDropdownItems.each(function () {
|
||||
const item = $(this);
|
||||
|
|
|
|||
|
|
@ -263,19 +263,6 @@ $(document).ready(function () {
|
|||
return configs;
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: `<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters"${
|
||||
configTypeSelection || configServiceSelection ? ' class="d-none"' : ""
|
||||
}>Show</span><span id="hide-filters"${
|
||||
!configTypeSelection && !configServiceSelection ? ' class="d-none"' : ""
|
||||
}>Hide</span><span class="d-none d-md-inline"> filters</span>`,
|
||||
action: function (e, dt, node, config) {
|
||||
configs_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.create_config = {
|
||||
text: '<span class="tf-icons bx bx-plus"></span><span class="d-none d-md-inline"> Create new custom config</span>',
|
||||
className: `btn btn-sm rounded me-4 btn-bw-green${
|
||||
|
|
@ -315,148 +302,155 @@ $(document).ready(function () {
|
|||
},
|
||||
};
|
||||
|
||||
const configs_table = new DataTable("#configs", {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
render: DataTable.render.select(),
|
||||
targets: 1,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
targets: -1,
|
||||
},
|
||||
{
|
||||
visible: false,
|
||||
targets: 7,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-window-alt"></i>HTTP',
|
||||
value: function (rowData, rowIdx) {
|
||||
$(rowData[3]).text().trim() === "HTTP";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-window-alt"></i>SERVER_HTTP',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "SERVER_HTTP";
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-window-alt"></i>DEFAULT_SERVER_HTTP',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "DEFAULT_SERVER_HTTP";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-shield-quarter"></i>MODSEC_CRS',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "MODSEC_CRS";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-shield-alt-2"></i>MODSEC',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "MODSEC";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-network-chart"></i>STREAM',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "STREAM";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-network-chart"></i>SERVER_STREAM',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "SERVER_STREAM";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-shield-alt"></i>CRS_PLUGINS_BEFORE',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "CRS_PLUGINS_BEFORE";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-shield-alt"></i>CRS_PLUGINS_AFTER',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "CRS_PLUGINS_AFTER";
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
},
|
||||
targets: 3,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
const configs_table = initializeDataTable({
|
||||
tableSelector: "#configs",
|
||||
tableName: "configs",
|
||||
columnVisibilityCondition: (column) => column > 2 && column < 8,
|
||||
dataTableOptions: {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
targets: 4,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
options: servicesSearchPanesOptions,
|
||||
{
|
||||
orderable: false,
|
||||
render: DataTable.render.select(),
|
||||
targets: 1,
|
||||
},
|
||||
targets: 5,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
options: templatesSearchPanesOptions,
|
||||
{
|
||||
orderable: false,
|
||||
targets: -1,
|
||||
},
|
||||
targets: 6,
|
||||
},
|
||||
],
|
||||
order: [[2, "asc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
select: {
|
||||
style: "multi+shift",
|
||||
selector: "td:nth-child(2)",
|
||||
headerCheckbox: true,
|
||||
},
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ custom configs",
|
||||
infoEmpty: "No custom configs available",
|
||||
infoFiltered: "(filtered from _MAX_ total custom configs)",
|
||||
lengthMenu: "Display _MENU_ custom configs",
|
||||
zeroRecords: "No matching custom configs found",
|
||||
{
|
||||
visible: false,
|
||||
targets: 7,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-window-alt"></i>HTTP',
|
||||
value: function (rowData, rowIdx) {
|
||||
$(rowData[3]).text().trim() === "HTTP";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-window-alt"></i>SERVER_HTTP',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "SERVER_HTTP";
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-window-alt"></i>DEFAULT_SERVER_HTTP',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "DEFAULT_SERVER_HTTP";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-shield-quarter"></i>MODSEC_CRS',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "MODSEC_CRS";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-shield-alt-2"></i>MODSEC',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "MODSEC";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-network-chart"></i>STREAM',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "STREAM";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-network-chart"></i>SERVER_STREAM',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "SERVER_STREAM";
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-shield-alt"></i>CRS_PLUGINS_BEFORE',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "CRS_PLUGINS_BEFORE";
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-shield-alt"></i>CRS_PLUGINS_AFTER',
|
||||
value: function (rowData, rowIdx) {
|
||||
return $(rowData[3]).text().trim() === "CRS_PLUGINS_AFTER";
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
},
|
||||
targets: 3,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 4,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
options: servicesSearchPanesOptions,
|
||||
},
|
||||
targets: 5,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
options: templatesSearchPanesOptions,
|
||||
},
|
||||
targets: 6,
|
||||
},
|
||||
],
|
||||
order: [[2, "asc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
select: {
|
||||
rows: {
|
||||
_: "Selected %d custom configs",
|
||||
0: "No custom configs selected",
|
||||
1: "Selected 1 custom config",
|
||||
style: "multi+shift",
|
||||
selector: "td:nth-child(2)",
|
||||
headerCheckbox: true,
|
||||
},
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ custom configs",
|
||||
infoEmpty: "No custom configs available",
|
||||
infoFiltered: "(filtered from _MAX_ total custom configs)",
|
||||
lengthMenu: "Display _MENU_ custom configs",
|
||||
zeroRecords: "No matching custom configs found",
|
||||
select: {
|
||||
rows: {
|
||||
_: "Selected %d custom configs",
|
||||
0: "No custom configs selected",
|
||||
1: "Selected 1 custom config",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#configs_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
if (isReadOnly)
|
||||
$("#configs_wrapper .dt-buttons")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"The database is in readonly, therefore you cannot create new custom configurations.",
|
||||
)
|
||||
.attr("data-bs-placement", "right")
|
||||
.tooltip();
|
||||
initComplete: function (settings, json) {
|
||||
$("#configs_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
if (isReadOnly)
|
||||
$("#configs_wrapper .dt-buttons")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"The database is in readonly, therefore you cannot create new custom configurations.",
|
||||
)
|
||||
.attr("data-bs-placement", "right")
|
||||
.tooltip();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -468,155 +462,12 @@ $(document).ready(function () {
|
|||
"click",
|
||||
);
|
||||
|
||||
if (!configTypeSelection && !configServiceSelection)
|
||||
configs_table.searchPanes.container().hide();
|
||||
|
||||
$(".action-button")
|
||||
.parent()
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
|
||||
$("#configs").removeClass("d-none");
|
||||
$("#configs-waiting").addClass("visually-hidden");
|
||||
|
||||
const defaultColsVisibility = JSON.parse(
|
||||
$("#columns_preferences_defaults").val().trim(),
|
||||
);
|
||||
|
||||
var columnVisibility = localStorage.getItem("bw-configs-columns");
|
||||
if (columnVisibility === null) {
|
||||
columnVisibility = JSON.parse($("#columns_preferences").val().trim());
|
||||
} else {
|
||||
columnVisibility = JSON.parse(columnVisibility);
|
||||
if (configTypeSelection || configServiceSelection) {
|
||||
configs_table.searchPanes.container().show();
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
}
|
||||
|
||||
Object.entries(columnVisibility).forEach(([key, value]) => {
|
||||
configs_table.column(key).visible(value);
|
||||
});
|
||||
|
||||
configs_table.responsive.recalc();
|
||||
|
||||
configs_table.on("mouseenter", "td", function () {
|
||||
if (configs_table.cell(this).index() === undefined) return;
|
||||
const rowIdx = configs_table.cell(this).index().row;
|
||||
|
||||
configs_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
|
||||
configs_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each(function (el) {
|
||||
if (configs_table.cell(el).index().row === rowIdx)
|
||||
el.classList.add("highlight");
|
||||
});
|
||||
});
|
||||
|
||||
configs_table.on("mouseleave", "td", function () {
|
||||
configs_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
configs_table.on("select", function (e, dt, type, indexes) {
|
||||
// Enable the actions button
|
||||
$(".action-button")
|
||||
.removeClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", null)
|
||||
.attr("data-bs-original-title", null)
|
||||
.attr("data-bs-placement", null)
|
||||
.tooltip("dispose");
|
||||
});
|
||||
|
||||
configs_table.on("deselect", function (e, dt, type, indexes) {
|
||||
// If no rows are selected, disable the actions button
|
||||
if (configs_table.rows({ selected: true }).count() === 0) {
|
||||
$(".action-button")
|
||||
.addClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", "tooltip")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
}
|
||||
});
|
||||
|
||||
const debounce = (func, delay) => {
|
||||
let timer = null;
|
||||
|
||||
return (...args) => {
|
||||
// Clear the timer if the function is called again during the delay
|
||||
if (timer) clearTimeout(timer);
|
||||
|
||||
// Start a new timer to invoke the function after the delay
|
||||
timer = setTimeout(() => {
|
||||
func(...args);
|
||||
}, delay);
|
||||
};
|
||||
};
|
||||
|
||||
const saveColumnsPreferences = debounce(() => {
|
||||
const rootUrl = $("#home-path")
|
||||
.val()
|
||||
.trim()
|
||||
.replace(/\/home$/, "/set_columns_preferences");
|
||||
const csrfToken = $("#csrf_token").val();
|
||||
|
||||
const data = new FormData();
|
||||
data.append("csrf_token", csrfToken);
|
||||
data.append("table_name", "configs");
|
||||
data.append("columns_preferences", JSON.stringify(columnVisibility));
|
||||
|
||||
fetch(rootUrl, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
console.log("Preferences saved successfully!");
|
||||
// Handle success, redirect, etc.
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("There was a problem with the fetch operation:", error);
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
configs_table.on(
|
||||
"column-visibility.dt",
|
||||
function (e, settings, column, state) {
|
||||
if (column === 0 || column === 1 || column === 8) return;
|
||||
columnVisibility[column] = state;
|
||||
// Check if columVisibility is equal to defaultColsVisibility
|
||||
const isDefault =
|
||||
JSON.stringify(columnVisibility) ===
|
||||
JSON.stringify(defaultColsVisibility);
|
||||
// If it is, remove the key from localStorage
|
||||
if (isDefault) {
|
||||
localStorage.removeItem("bw-configs-columns");
|
||||
} else {
|
||||
localStorage.setItem(
|
||||
"bw-configs-columns",
|
||||
JSON.stringify(columnVisibility),
|
||||
);
|
||||
}
|
||||
|
||||
saveColumnsPreferences();
|
||||
},
|
||||
);
|
||||
|
||||
$(document).on("click", ".delete-config", function () {
|
||||
if (isReadOnly) {
|
||||
alert("This action is not allowed in read-only mode.");
|
||||
|
|
|
|||
|
|
@ -263,14 +263,6 @@ $(document).ready(function () {
|
|||
},
|
||||
];
|
||||
|
||||
$(document).on("hidden.bs.toast", ".toast", function (event) {
|
||||
if (event.target.id.startsWith("feedback-toast")) {
|
||||
setTimeout(() => {
|
||||
$(this).remove();
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
$("#modal-delete-instances").on("hidden.bs.modal", function () {
|
||||
$("#selected-instances").empty();
|
||||
$("#selected-instances-input").val("");
|
||||
|
|
@ -303,15 +295,6 @@ $(document).ready(function () {
|
|||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
instances_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.ping_instances = {
|
||||
text: '<span class="tf-icons bx bx-bell bx-18px me-2"></span>Ping',
|
||||
action: function (e, dt, node, config) {
|
||||
|
|
@ -382,358 +365,215 @@ $(document).ready(function () {
|
|||
},
|
||||
};
|
||||
|
||||
const instances_table = new DataTable("#instances", {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
render: DataTable.render.select(),
|
||||
targets: 1,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
targets: -1,
|
||||
},
|
||||
{
|
||||
visible: false,
|
||||
targets: [3, 4],
|
||||
},
|
||||
{
|
||||
targets: [7, 8],
|
||||
render: function (data, type, row) {
|
||||
if (type === "display" || type === "filter") {
|
||||
const date = new Date(data);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date.toLocaleString();
|
||||
initializeDataTable({
|
||||
tableSelector: "#instances",
|
||||
tableName: "instances",
|
||||
columnVisibilityCondition: (column) => column > 2 && column < 9,
|
||||
dataTableOptions: {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
render: DataTable.render.select(),
|
||||
targets: 1,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
targets: -1,
|
||||
},
|
||||
{
|
||||
visible: false,
|
||||
targets: [3, 4],
|
||||
},
|
||||
{
|
||||
targets: [7, 8],
|
||||
render: function (data, type, row) {
|
||||
if (type === "display" || type === "filter") {
|
||||
const date = new Date(data);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date.toLocaleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
return data;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: [4],
|
||||
},
|
||||
targets: [4],
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-up-arrow-alt text-success"></i> Up',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[5].includes("Up");
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-up-arrow-alt text-success"></i> Up',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[5].includes("Up");
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-down-arrow-alt text-danger"></i> Down',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[5].includes("Down");
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-down-arrow-alt text-danger"></i> Down',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[5].includes("Down");
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bxs-hourglass text-warning"></i> Loading',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[5].includes("Loading");
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bxs-hourglass text-warning"></i> Loading',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[5].includes("Loading");
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 5,
|
||||
},
|
||||
targets: 5,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-microchip"></i> Static',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("Static");
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-microchip"></i> Static',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("Static");
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bxl-docker"></i> Container',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("Container");
|
||||
{
|
||||
label: '<i class="bx bx-xs bxl-docker"></i> Container',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("Container");
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bxl-kubernetes"></i> Pod',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("Pod");
|
||||
{
|
||||
label: '<i class="bx bx-xs bxl-kubernetes"></i> Pod',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("Pod");
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 6,
|
||||
},
|
||||
targets: 6,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Last 24 hours",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[7]);
|
||||
const now = new Date();
|
||||
return now - date < 24 * 60 * 60 * 1000;
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Last 24 hours",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[7]);
|
||||
const now = new Date();
|
||||
return now - date < 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Last 7 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[7]);
|
||||
const now = new Date();
|
||||
return now - date < 7 * 24 * 60 * 60 * 1000;
|
||||
{
|
||||
label: "Last 7 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[7]);
|
||||
const now = new Date();
|
||||
return now - date < 7 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Last 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[7]);
|
||||
const now = new Date();
|
||||
return now - date < 30 * 24 * 60 * 60 * 1000;
|
||||
{
|
||||
label: "Last 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[7]);
|
||||
const now = new Date();
|
||||
return now - date < 30 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 7,
|
||||
},
|
||||
targets: 7,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Last 24 hours",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[8]);
|
||||
const now = new Date();
|
||||
return now - date < 24 * 60 * 60 * 1000;
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Last 24 hours",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[8]);
|
||||
const now = new Date();
|
||||
return now - date < 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Last 7 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[8]);
|
||||
const now = new Date();
|
||||
return now - date < 7 * 24 * 60 * 60 * 1000;
|
||||
{
|
||||
label: "Last 7 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[8]);
|
||||
const now = new Date();
|
||||
return now - date < 7 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Last 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[8]);
|
||||
const now = new Date();
|
||||
return now - date < 30 * 24 * 60 * 60 * 1000;
|
||||
{
|
||||
label: "Last 30 days",
|
||||
value: function (rowData, rowIdx) {
|
||||
const date = new Date(rowData[8]);
|
||||
const now = new Date();
|
||||
return now - date < 30 * 24 * 60 * 60 * 1000;
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 8,
|
||||
},
|
||||
targets: 8,
|
||||
},
|
||||
],
|
||||
order: [[8, "desc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
select: {
|
||||
style: "multi+shift",
|
||||
selector: "td:nth-child(2)",
|
||||
headerCheckbox: true,
|
||||
},
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ instances",
|
||||
infoEmpty: "No instances available",
|
||||
infoFiltered: "(filtered from _MAX_ total instances)",
|
||||
lengthMenu: "Display _MENU_ instances",
|
||||
zeroRecords: "No matching instances found",
|
||||
],
|
||||
order: [[8, "desc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
select: {
|
||||
rows: {
|
||||
_: "Selected %d instances",
|
||||
0: "No instances selected",
|
||||
1: "Selected 1 instance",
|
||||
style: "multi+shift",
|
||||
selector: "td:nth-child(2)",
|
||||
headerCheckbox: true,
|
||||
},
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ instances",
|
||||
infoEmpty: "No instances available",
|
||||
infoFiltered: "(filtered from _MAX_ total instances)",
|
||||
lengthMenu: "Display _MENU_ instances",
|
||||
zeroRecords: "No matching instances found",
|
||||
select: {
|
||||
rows: {
|
||||
_: "Selected %d instances",
|
||||
0: "No instances selected",
|
||||
1: "Selected 1 instance",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#instances_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
if (isReadOnly)
|
||||
$("#instances_wrapper .dt-buttons")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"The database is in readonly, therefore you cannot create new instances.",
|
||||
)
|
||||
.attr("data-bs-placement", "right")
|
||||
.tooltip();
|
||||
initComplete: function (settings, json) {
|
||||
$("#instances_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
if (isReadOnly)
|
||||
$("#instances_wrapper .dt-buttons")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"The database is in readonly, therefore you cannot create new instances.",
|
||||
)
|
||||
.attr("data-bs-placement", "right")
|
||||
.tooltip();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
instances_table.searchPanes.container().hide();
|
||||
|
||||
$(".action-button")
|
||||
.parent()
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
|
||||
$("#instances").removeClass("d-none");
|
||||
$("#instances-waiting").addClass("visually-hidden");
|
||||
|
||||
const defaultColsVisibility = JSON.parse(
|
||||
$("#columns_preferences_defaults").val().trim(),
|
||||
);
|
||||
|
||||
var columnVisibility = localStorage.getItem("bw-instances-columns");
|
||||
if (columnVisibility === null) {
|
||||
columnVisibility = JSON.parse($("#columns_preferences").val().trim());
|
||||
} else {
|
||||
columnVisibility = JSON.parse(columnVisibility);
|
||||
}
|
||||
|
||||
Object.entries(columnVisibility).forEach(([key, value]) => {
|
||||
instances_table.column(key).visible(value);
|
||||
});
|
||||
|
||||
instances_table.responsive.recalc();
|
||||
|
||||
instances_table.on("mouseenter", "td", function () {
|
||||
if (instances_table.cell(this).index() === undefined) return;
|
||||
const rowIdx = instances_table.cell(this).index().row;
|
||||
|
||||
instances_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
|
||||
instances_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each(function (el) {
|
||||
if (instances_table.cell(el).index().row === rowIdx)
|
||||
el.classList.add("highlight");
|
||||
});
|
||||
});
|
||||
|
||||
instances_table.on("mouseleave", "td", function () {
|
||||
instances_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
instances_table.on("select", function (e, dt, type, indexes) {
|
||||
// Enable the actions button
|
||||
$(".action-button")
|
||||
.removeClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", null)
|
||||
.attr("data-bs-original-title", null)
|
||||
.attr("data-bs-placement", null)
|
||||
.tooltip("dispose");
|
||||
});
|
||||
|
||||
instances_table.on("deselect", function (e, dt, type, indexes) {
|
||||
// If no rows are selected, disable the actions button
|
||||
if (instances_table.rows({ selected: true }).count() === 0) {
|
||||
$(".action-button")
|
||||
.addClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", "tooltip")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
}
|
||||
});
|
||||
|
||||
const debounce = (func, delay) => {
|
||||
let timer = null;
|
||||
|
||||
return (...args) => {
|
||||
// Clear the timer if the function is called again during the delay
|
||||
if (timer) clearTimeout(timer);
|
||||
|
||||
// Start a new timer to invoke the function after the delay
|
||||
timer = setTimeout(() => {
|
||||
func(...args);
|
||||
}, delay);
|
||||
};
|
||||
};
|
||||
|
||||
const saveColumnsPreferences = debounce(() => {
|
||||
const rootUrl = $("#home-path")
|
||||
.val()
|
||||
.trim()
|
||||
.replace(/\/home$/, "/set_columns_preferences");
|
||||
const csrfToken = $("#csrf_token").val();
|
||||
|
||||
const data = new FormData();
|
||||
data.append("csrf_token", csrfToken);
|
||||
data.append("table_name", "instances");
|
||||
data.append("columns_preferences", JSON.stringify(columnVisibility));
|
||||
|
||||
fetch(rootUrl, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
console.log("Preferences saved successfully!");
|
||||
// Handle success, redirect, etc.
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("There was a problem with the fetch operation:", error);
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
instances_table.on(
|
||||
"column-visibility.dt",
|
||||
function (e, settings, column, state) {
|
||||
if (column === 0 || column === 1 || column === 9) return;
|
||||
columnVisibility[column] = state;
|
||||
// Check if columVisibility is equal to defaultColsVisibility
|
||||
const isDefault =
|
||||
JSON.stringify(columnVisibility) ===
|
||||
JSON.stringify(defaultColsVisibility);
|
||||
// If it is, remove the key from localStorage
|
||||
if (isDefault) {
|
||||
localStorage.removeItem("bw-instances-columns");
|
||||
} else {
|
||||
localStorage.setItem(
|
||||
"bw-instances-columns",
|
||||
JSON.stringify(columnVisibility),
|
||||
);
|
||||
}
|
||||
|
||||
saveColumnsPreferences();
|
||||
},
|
||||
);
|
||||
|
||||
$(document).on("click", ".ping-instance", function () {
|
||||
if (actionLock) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -151,15 +151,6 @@ $(document).ready(function () {
|
|||
form.appendTo("body").submit();
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
jobs_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.run_jobs = {
|
||||
text: '<span class="tf-icons bx bx-play bx-18px me-2"></span>Run selected jobs',
|
||||
action: function (e, dt, node, config) {
|
||||
|
|
@ -205,303 +196,168 @@ $(document).ready(function () {
|
|||
}
|
||||
});
|
||||
|
||||
const jobs_table = new DataTable("#jobs", {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
render: DataTable.render.select(),
|
||||
targets: 1,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
targets: -1,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
},
|
||||
targets: 3,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
header: "Interval",
|
||||
options: [
|
||||
{
|
||||
label: "Every day",
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[4].includes("day");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Every hour",
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[4].includes("hour");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Every week",
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[4].includes("week");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Once",
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[4].includes("once");
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
initializeDataTable({
|
||||
tableSelector: "#jobs",
|
||||
tableName: "jobs",
|
||||
columnVisibilityCondition: (column) => column > 2 && column < 8,
|
||||
dataTableOptions: {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
targets: 4,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
header: "Reload",
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-x text-danger"></i> No',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[5].includes("bx-x");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-check text-success"></i> Yes',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[5].includes("bx-check");
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
{
|
||||
orderable: false,
|
||||
render: DataTable.render.select(),
|
||||
targets: 1,
|
||||
},
|
||||
targets: 5,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
header: "Async",
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-x text-danger"></i> No',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("bx-x");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-check text-success"></i> Yes',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("bx-check");
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
{
|
||||
orderable: false,
|
||||
targets: -1,
|
||||
},
|
||||
targets: 6,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
header: "Last run state",
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-x text-danger"></i> Failed',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[7].includes("bx-x");
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-check text-success"></i> Success',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[7].includes("bx-check");
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
},
|
||||
targets: 3,
|
||||
},
|
||||
targets: 7,
|
||||
},
|
||||
],
|
||||
order: [[3, "asc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
select: {
|
||||
style: "multi+shift",
|
||||
selector: "td:nth-child(2)",
|
||||
headerCheckbox: true,
|
||||
},
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ jobs",
|
||||
infoEmpty: "No jobs available",
|
||||
infoFiltered: "(filtered from _MAX_ total jobs)",
|
||||
lengthMenu: "Display _MENU_ jobs",
|
||||
zeroRecords: "No matching jobs found",
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
header: "Interval",
|
||||
options: [
|
||||
{
|
||||
label: "Every day",
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[4].includes("day");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Every hour",
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[4].includes("hour");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Every week",
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[4].includes("week");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Once",
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[4].includes("once");
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 4,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
header: "Reload",
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-x text-danger"></i> No',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[5].includes("bx-x");
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-check text-success"></i> Yes',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[5].includes("bx-check");
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 5,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
header: "Async",
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-x text-danger"></i> No',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("bx-x");
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-check text-success"></i> Yes',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("bx-check");
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 6,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
header: "Last run state",
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-x text-danger"></i> Failed',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[7].includes("bx-x");
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-check text-success"></i> Success',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[7].includes("bx-check");
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 7,
|
||||
},
|
||||
],
|
||||
order: [[3, "asc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
select: {
|
||||
rows: {
|
||||
_: "Selected %d jobs",
|
||||
0: "No jobs selected",
|
||||
1: "Selected 1 job",
|
||||
style: "multi+shift",
|
||||
selector: "td:nth-child(2)",
|
||||
headerCheckbox: true,
|
||||
},
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ jobs",
|
||||
infoEmpty: "No jobs available",
|
||||
infoFiltered: "(filtered from _MAX_ total jobs)",
|
||||
lengthMenu: "Display _MENU_ jobs",
|
||||
zeroRecords: "No matching jobs found",
|
||||
select: {
|
||||
rows: {
|
||||
_: "Selected %d jobs",
|
||||
0: "No jobs selected",
|
||||
1: "Selected 1 job",
|
||||
},
|
||||
},
|
||||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#jobs_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
},
|
||||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#jobs_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
},
|
||||
});
|
||||
|
||||
jobs_table.searchPanes.container().hide();
|
||||
|
||||
$(".action-button")
|
||||
.parent()
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
|
||||
$("#jobs").removeClass("d-none");
|
||||
$("#jobs-waiting").addClass("visually-hidden");
|
||||
|
||||
const defaultColsVisibility = JSON.parse(
|
||||
$("#columns_preferences_defaults").val().trim(),
|
||||
);
|
||||
|
||||
var columnVisibility = localStorage.getItem("bw-instances-columns");
|
||||
if (columnVisibility === null) {
|
||||
columnVisibility = JSON.parse($("#columns_preferences").val().trim());
|
||||
} else {
|
||||
columnVisibility = JSON.parse(columnVisibility);
|
||||
}
|
||||
|
||||
Object.entries(columnVisibility).forEach(([key, value]) => {
|
||||
instances_table.column(key).visible(value);
|
||||
});
|
||||
|
||||
jobs_table.responsive.recalc();
|
||||
|
||||
jobs_table.on("mouseenter", "td", function () {
|
||||
if (jobs_table.cell(this).index() === undefined) return;
|
||||
const rowIdx = jobs_table.cell(this).index().row;
|
||||
|
||||
jobs_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
|
||||
jobs_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each(function (el) {
|
||||
if (jobs_table.cell(el).index().row === rowIdx)
|
||||
el.classList.add("highlight");
|
||||
});
|
||||
});
|
||||
|
||||
jobs_table.on("mouseleave", "td", function () {
|
||||
jobs_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
jobs_table.on("select", function (e, dt, type, indexes) {
|
||||
// Enable the actions button
|
||||
$(".action-button")
|
||||
.removeClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", null)
|
||||
.attr("data-bs-original-title", null)
|
||||
.attr("data-bs-placement", null)
|
||||
.tooltip("dispose");
|
||||
});
|
||||
|
||||
jobs_table.on("deselect", function (e, dt, type, indexes) {
|
||||
// If no rows are selected, disable the actions button
|
||||
if (jobs_table.rows({ selected: true }).count() === 0) {
|
||||
$(".action-button")
|
||||
.addClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", "tooltip")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
}
|
||||
});
|
||||
|
||||
const debounce = (func, delay) => {
|
||||
let timer = null;
|
||||
|
||||
return (...args) => {
|
||||
// Clear the timer if the function is called again during the delay
|
||||
if (timer) clearTimeout(timer);
|
||||
|
||||
// Start a new timer to invoke the function after the delay
|
||||
timer = setTimeout(() => {
|
||||
func(...args);
|
||||
}, delay);
|
||||
};
|
||||
};
|
||||
|
||||
const saveColumnsPreferences = debounce(() => {
|
||||
const rootUrl = $("#home-path")
|
||||
.val()
|
||||
.trim()
|
||||
.replace(/\/home$/, "/set_columns_preferences");
|
||||
const csrfToken = $("#csrf_token").val();
|
||||
|
||||
const data = new FormData();
|
||||
data.append("csrf_token", csrfToken);
|
||||
data.append("table_name", "jobs");
|
||||
data.append("columns_preferences", JSON.stringify(columnVisibility));
|
||||
|
||||
fetch(rootUrl, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
console.log("Preferences saved successfully!");
|
||||
// Handle success, redirect, etc.
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("There was a problem with the fetch operation:", error);
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
jobs_table.on("column-visibility.dt", function (e, settings, column, state) {
|
||||
if (column < 3 || column === 8) return;
|
||||
columnVisibility[column] = state;
|
||||
// Check if columVisibility is equal to defaultColsVisibility
|
||||
const isDefault =
|
||||
JSON.stringify(columnVisibility) ===
|
||||
JSON.stringify(defaultColsVisibility);
|
||||
// If it is, remove the key from localStorage
|
||||
if (isDefault) {
|
||||
localStorage.removeItem("bw-jobs-columns");
|
||||
} else {
|
||||
localStorage.setItem("bw-jobs-columns", JSON.stringify(columnVisibility));
|
||||
}
|
||||
|
||||
saveColumnsPreferences();
|
||||
});
|
||||
|
||||
$(document).on("click", ".show-history", function () {
|
||||
|
|
|
|||
|
|
@ -70,12 +70,17 @@ $(document).ready(function () {
|
|||
});
|
||||
}
|
||||
|
||||
new DataTable(this, {
|
||||
columnDefs: columnDefs,
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
layout: layout,
|
||||
order: [[parseInt(tableOrder.column), tableOrder.dir]],
|
||||
initializeDataTable({
|
||||
tableSelector: this,
|
||||
tableName: this.id,
|
||||
columnVisibilityCondition: (column) => false, // Ignore all columns
|
||||
dataTableOptions: {
|
||||
columnDefs: columnDefs,
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
layout: layout,
|
||||
order: [[parseInt(tableOrder.column), tableOrder.dir]],
|
||||
},
|
||||
});
|
||||
|
||||
$(this).removeClass("d-none");
|
||||
|
|
|
|||
|
|
@ -280,15 +280,6 @@ $(document).ready(function () {
|
|||
return plugins;
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
plugins_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.add_plugin = {
|
||||
text: '<span class="tf-icons bx bx-plus"></span><span class="d-none d-md-inline"> Add plugin(s)</span>',
|
||||
className: `btn btn-sm rounded me-4 btn-bw-green${
|
||||
|
|
@ -330,285 +321,144 @@ $(document).ready(function () {
|
|||
},
|
||||
};
|
||||
|
||||
const plugins_table = new DataTable("#plugins", {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
render: DataTable.render.select(),
|
||||
targets: 1,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
targets: -1,
|
||||
},
|
||||
{
|
||||
visible: false,
|
||||
targets: [2, 4],
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
header: "Stream Support",
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-x text-danger"></i> No',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("bx-x");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-check text-success"></i> Yes',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("bx-check");
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-minus text-warning"></i> Partial',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("bx-minus");
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
initializeDataTable({
|
||||
tableSelector: "#plugins",
|
||||
tableName: "plugins",
|
||||
columnVisibilityCondition: (column) =>
|
||||
column > 1 && column !== 3 && column < 9,
|
||||
dataTableOptions: {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
targets: 6,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: `<img src="${$("#pro_diamond_url")
|
||||
.val()
|
||||
.trim()}" alt="Pro plugin" width="16px" height="12.9125px" class="mb-1"> PRO`,
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[7].includes("PRO");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-plug bx-xs"></i> External',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[7].includes("EXTERNAL");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-cloud-upload bx-xs"></i> UI',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[7].includes("UI");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-shield bx-xs"></i> Core',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[7].includes("CORE");
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
{
|
||||
orderable: false,
|
||||
render: DataTable.render.select(),
|
||||
targets: 1,
|
||||
},
|
||||
targets: 7,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
{
|
||||
orderable: false,
|
||||
targets: -1,
|
||||
},
|
||||
targets: 8,
|
||||
},
|
||||
],
|
||||
order: [[3, "asc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
select: {
|
||||
style: "multi+shift",
|
||||
selector: "td:nth-child(2)",
|
||||
headerCheckbox: true,
|
||||
},
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ plugins",
|
||||
infoEmpty: "No plugins available",
|
||||
infoFiltered: "(filtered from _MAX_ total plugins)",
|
||||
lengthMenu: "Display _MENU_ plugins",
|
||||
zeroRecords: "No matching plugins found",
|
||||
{
|
||||
visible: false,
|
||||
targets: [2, 4],
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
header: "Stream Support",
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-x text-danger"></i> No',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("bx-x");
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-check text-success"></i> Yes',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("bx-check");
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-minus text-warning"></i> Partial',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[6].includes("bx-minus");
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 6,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: `<img src="${$("#pro_diamond_url")
|
||||
.val()
|
||||
.trim()}" alt="Pro plugin" width="16px" height="12.9125px" class="mb-1"> PRO`,
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[7].includes("PRO");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-plug bx-xs"></i> External',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[7].includes("EXTERNAL");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-cloud-upload bx-xs"></i> UI',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[7].includes("UI");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-shield bx-xs"></i> Core',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[7].includes("CORE");
|
||||
},
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 7,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 8,
|
||||
},
|
||||
],
|
||||
order: [[3, "asc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
select: {
|
||||
rows: {
|
||||
_: "Selected %d plugins",
|
||||
0: "No plugins selected",
|
||||
1: "Selected 1 plugin",
|
||||
style: "multi+shift",
|
||||
selector: "td:nth-child(2)",
|
||||
headerCheckbox: true,
|
||||
},
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ plugins",
|
||||
infoEmpty: "No plugins available",
|
||||
infoFiltered: "(filtered from _MAX_ total plugins)",
|
||||
lengthMenu: "Display _MENU_ plugins",
|
||||
zeroRecords: "No matching plugins found",
|
||||
select: {
|
||||
rows: {
|
||||
_: "Selected %d plugins",
|
||||
0: "No plugins selected",
|
||||
1: "Selected 1 plugin",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#plugins_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
if (isReadOnly)
|
||||
$("#plugins_wrapper .dt-buttons")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"The database is in readonly, therefore you cannot create add plugins.",
|
||||
)
|
||||
.attr("data-bs-placement", "right")
|
||||
.tooltip();
|
||||
initComplete: function (settings, json) {
|
||||
$("#plugins_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
if (isReadOnly)
|
||||
$("#plugins_wrapper .dt-buttons")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"The database is in readonly, therefore you cannot create add plugins.",
|
||||
)
|
||||
.attr("data-bs-placement", "right")
|
||||
.tooltip();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
plugins_table.searchPanes.container().hide();
|
||||
|
||||
$(".action-button")
|
||||
.parent()
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
|
||||
$("#plugins").removeClass("d-none");
|
||||
$("#plugins-waiting").addClass("visually-hidden");
|
||||
|
||||
const defaultColsVisibility = JSON.parse(
|
||||
$("#columns_preferences_defaults").val().trim(),
|
||||
);
|
||||
|
||||
var columnVisibility = localStorage.getItem("bw-plugins-columns");
|
||||
if (columnVisibility === null) {
|
||||
columnVisibility = JSON.parse($("#columns_preferences").val().trim());
|
||||
} else {
|
||||
columnVisibility = JSON.parse(columnVisibility);
|
||||
}
|
||||
|
||||
Object.entries(columnVisibility).forEach(([key, value]) => {
|
||||
plugins_table.column(key).visible(value);
|
||||
});
|
||||
|
||||
plugins_table.responsive.recalc();
|
||||
|
||||
plugins_table.on("mouseenter", "td", function () {
|
||||
if (plugins_table.cell(this).index() === undefined) return;
|
||||
const rowIdx = plugins_table.cell(this).index().row;
|
||||
|
||||
plugins_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
|
||||
plugins_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each(function (el) {
|
||||
if (plugins_table.cell(el).index().row === rowIdx)
|
||||
el.classList.add("highlight");
|
||||
});
|
||||
});
|
||||
|
||||
plugins_table.on("mouseleave", "td", function () {
|
||||
plugins_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
plugins_table.on("select", function (e, dt, type, indexes) {
|
||||
// Enable the actions button
|
||||
$(".action-button")
|
||||
.removeClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", null)
|
||||
.attr("data-bs-original-title", null)
|
||||
.attr("data-bs-placement", null)
|
||||
.tooltip("dispose");
|
||||
});
|
||||
|
||||
plugins_table.on("deselect", function (e, dt, type, indexes) {
|
||||
// If no rows are selected, disable the actions button
|
||||
if (plugins_table.rows({ selected: true }).count() === 0) {
|
||||
$(".action-button")
|
||||
.addClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", "tooltip")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
}
|
||||
});
|
||||
|
||||
const debounce = (func, delay) => {
|
||||
let timer = null;
|
||||
|
||||
return (...args) => {
|
||||
// Clear the timer if the function is called again during the delay
|
||||
if (timer) clearTimeout(timer);
|
||||
|
||||
// Start a new timer to invoke the function after the delay
|
||||
timer = setTimeout(() => {
|
||||
func(...args);
|
||||
}, delay);
|
||||
};
|
||||
};
|
||||
|
||||
const saveColumnsPreferences = debounce(() => {
|
||||
const rootUrl = $("#home-path")
|
||||
.val()
|
||||
.trim()
|
||||
.replace(/\/home$/, "/set_columns_preferences");
|
||||
const csrfToken = $("#csrf_token").val();
|
||||
|
||||
const data = new FormData();
|
||||
data.append("csrf_token", csrfToken);
|
||||
data.append("table_name", "plugins");
|
||||
data.append("columns_preferences", JSON.stringify(columnVisibility));
|
||||
|
||||
fetch(rootUrl, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
console.log("Preferences saved successfully!");
|
||||
// Handle success, redirect, etc.
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("There was a problem with the fetch operation:", error);
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
plugins_table.on(
|
||||
"column-visibility.dt",
|
||||
function (e, settings, column, state) {
|
||||
if (column === 0 || column === 1 || column === 9) return;
|
||||
columnVisibility[column] = state;
|
||||
// Check if columVisibility is equal to defaultColsVisibility
|
||||
const isDefault =
|
||||
JSON.stringify(columnVisibility) ===
|
||||
JSON.stringify(defaultColsVisibility);
|
||||
// If it is, remove the key from localStorage
|
||||
if (isDefault) {
|
||||
localStorage.removeItem("bw-plugins-columns");
|
||||
} else {
|
||||
localStorage.setItem(
|
||||
"bw-plugins-columns",
|
||||
JSON.stringify(columnVisibility),
|
||||
);
|
||||
}
|
||||
|
||||
saveColumnsPreferences();
|
||||
},
|
||||
);
|
||||
|
||||
$(document).on("click", ".delete-plugin", function () {
|
||||
if (isReadOnly) {
|
||||
alert("This action is not allowed in read-only mode.");
|
||||
|
|
|
|||
|
|
@ -390,15 +390,6 @@ $(function () {
|
|||
},
|
||||
];
|
||||
|
||||
// Custom button for toggling filters
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: (e, dt, node, config) => {
|
||||
reports_table.searchPanes.container().slideToggle();
|
||||
$("#show-filters, #hide-filters").toggleClass("d-none");
|
||||
},
|
||||
};
|
||||
|
||||
// Custom button for auto-refresh
|
||||
let autoRefresh = false;
|
||||
const sessionAutoRefresh = sessionStorage.getItem("reportsAutoRefresh");
|
||||
|
|
@ -436,156 +427,72 @@ $(function () {
|
|||
};
|
||||
|
||||
// Initialize DataTable
|
||||
const reports_table = new DataTable("#reports", {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
{ orderable: false, targets: -1 },
|
||||
{ visible: false, targets: [4, 5, 6, 7, 10] },
|
||||
{ type: "ip-address", targets: 2 },
|
||||
{
|
||||
render: function (data, type, row) {
|
||||
if (type === "display" || type === "filter") {
|
||||
const date = new Date(data);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date.toLocaleString();
|
||||
const reports_table = initializeDataTable({
|
||||
tableSelector: "#reports",
|
||||
tableName: "reports",
|
||||
columnVisibilityCondition: (column) => column > 2 && column < 12,
|
||||
dataTableOptions: {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
{ orderable: false, targets: -1 },
|
||||
{ visible: false, targets: [4, 5, 6, 7, 10] },
|
||||
{ type: "ip-address", targets: 2 },
|
||||
{
|
||||
render: function (data, type, row) {
|
||||
if (type === "display" || type === "filter") {
|
||||
const date = new Date(data);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date.toLocaleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
return data;
|
||||
},
|
||||
targets: 1,
|
||||
},
|
||||
targets: 1,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
options: countriesSearchPanesOptions,
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
options: countriesSearchPanesOptions,
|
||||
},
|
||||
targets: 3,
|
||||
},
|
||||
targets: 3,
|
||||
{
|
||||
searchPanes: { show: true },
|
||||
targets: [2, 4, 5, 6, 8, 9, 11],
|
||||
},
|
||||
],
|
||||
order: [[1, "desc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ reports",
|
||||
infoEmpty: "No reports available",
|
||||
infoFiltered: "(filtered from _MAX_ total reports)",
|
||||
lengthMenu: "Display _MENU_ reports",
|
||||
zeroRecords: "No matching reports found",
|
||||
},
|
||||
{
|
||||
searchPanes: { show: true },
|
||||
targets: [2, 4, 5, 6, 8, 9, 11],
|
||||
initComplete: () => {
|
||||
$("#reports_wrapper")
|
||||
.find(".btn-secondary")
|
||||
.removeClass("btn-secondary");
|
||||
updateCountryTooltips();
|
||||
},
|
||||
],
|
||||
order: [[1, "desc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ reports",
|
||||
infoEmpty: "No reports available",
|
||||
infoFiltered: "(filtered from _MAX_ total reports)",
|
||||
lengthMenu: "Display _MENU_ reports",
|
||||
zeroRecords: "No matching reports found",
|
||||
},
|
||||
initComplete: () => {
|
||||
$("#reports_wrapper").find(".btn-secondary").removeClass("btn-secondary");
|
||||
updateCountryTooltips();
|
||||
},
|
||||
});
|
||||
|
||||
$(".dt-type-numeric").removeClass("dt-type-numeric");
|
||||
|
||||
if (sessionAutoRefresh === "true") {
|
||||
toggleAutoRefresh();
|
||||
}
|
||||
|
||||
// Initially hide search panes
|
||||
reports_table.searchPanes.container().hide();
|
||||
|
||||
// Show the reports table and hide the loading indicator
|
||||
$("#reports").removeClass("d-none");
|
||||
$("#reports-waiting").addClass("visually-hidden");
|
||||
|
||||
const defaultColsVisibility = JSON.parse(
|
||||
$("#columns_preferences_defaults").val().trim(),
|
||||
);
|
||||
|
||||
var columnVisibility = localStorage.getItem("bw-reports-columns");
|
||||
if (columnVisibility === null) {
|
||||
columnVisibility = JSON.parse($("#columns_preferences").val().trim());
|
||||
} else {
|
||||
columnVisibility = JSON.parse(columnVisibility);
|
||||
}
|
||||
|
||||
Object.entries(columnVisibility).forEach(([key, value]) => {
|
||||
reports_table.column(key).visible(value);
|
||||
});
|
||||
|
||||
reports_table.responsive.recalc();
|
||||
|
||||
// Update tooltips after table draw
|
||||
reports_table.on("draw.dt", updateCountryTooltips);
|
||||
|
||||
const debounce = (func, delay) => {
|
||||
let timer = null;
|
||||
|
||||
return (...args) => {
|
||||
// Clear the timer if the function is called again during the delay
|
||||
if (timer) clearTimeout(timer);
|
||||
|
||||
// Start a new timer to invoke the function after the delay
|
||||
timer = setTimeout(() => {
|
||||
func(...args);
|
||||
}, delay);
|
||||
};
|
||||
};
|
||||
|
||||
const saveColumnsPreferences = debounce(() => {
|
||||
const rootUrl = $("#home-path")
|
||||
.val()
|
||||
.trim()
|
||||
.replace(/\/home$/, "/set_columns_preferences");
|
||||
const csrfToken = $("#csrf_token").val();
|
||||
|
||||
const data = new FormData();
|
||||
data.append("csrf_token", csrfToken);
|
||||
data.append("table_name", "reports");
|
||||
data.append("columns_preferences", JSON.stringify(columnVisibility));
|
||||
|
||||
fetch(rootUrl, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
console.log("Preferences saved successfully!");
|
||||
// Handle success, redirect, etc.
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("There was a problem with the fetch operation:", error);
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
reports_table.on(
|
||||
"column-visibility.dt",
|
||||
function (e, settings, column, state) {
|
||||
if (column < 3) return;
|
||||
columnVisibility[column] = state;
|
||||
// Check if columVisibility is equal to defaultColsVisibility
|
||||
const isDefault =
|
||||
JSON.stringify(columnVisibility) ===
|
||||
JSON.stringify(defaultColsVisibility);
|
||||
// If it is, remove the key from localStorage
|
||||
if (isDefault) {
|
||||
localStorage.removeItem("bw-reports-columns");
|
||||
} else {
|
||||
localStorage.setItem(
|
||||
"bw-reports-columns",
|
||||
JSON.stringify(columnVisibility),
|
||||
);
|
||||
}
|
||||
|
||||
saveColumnsPreferences();
|
||||
},
|
||||
);
|
||||
|
||||
const hashValue = location.hash;
|
||||
if (hashValue) {
|
||||
$("#dt-length-0").val(hashValue.replace("#", ""));
|
||||
|
|
|
|||
|
|
@ -227,15 +227,6 @@ $(function () {
|
|||
})
|
||||
.get();
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
services_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.create_service = {
|
||||
text: '<span class="tf-icons bx bx-plus"></span><span class="d-none d-md-inline"> Create new service</span>',
|
||||
className: `btn btn-sm rounded me-4 btn-bw-green${
|
||||
|
|
@ -334,296 +325,157 @@ $(function () {
|
|||
},
|
||||
};
|
||||
|
||||
const services_table = new DataTable("#services", {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
render: DataTable.render.select(),
|
||||
targets: 1,
|
||||
},
|
||||
{ orderable: false, targets: -1 },
|
||||
{
|
||||
targets: [5, 6],
|
||||
render: function (data, type, row) {
|
||||
if (type === "display" || type === "filter") {
|
||||
const date = new Date(data);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date.toLocaleString();
|
||||
initializeDataTable({
|
||||
tableSelector: "#services",
|
||||
tableName: "services",
|
||||
columnVisibilityCondition: (column) => column > 2 && column < 7,
|
||||
dataTableOptions: {
|
||||
columnDefs: [
|
||||
{
|
||||
orderable: false,
|
||||
className: "dtr-control",
|
||||
targets: 0,
|
||||
},
|
||||
{
|
||||
orderable: false,
|
||||
render: DataTable.render.select(),
|
||||
targets: 1,
|
||||
},
|
||||
{ orderable: false, targets: -1 },
|
||||
{
|
||||
targets: [5, 6],
|
||||
render: function (data, type, row) {
|
||||
if (type === "display" || type === "filter") {
|
||||
const date = new Date(data);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date.toLocaleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
return data;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-globe"></i> Online',
|
||||
value: (rowData) => rowData[3].includes("Online"),
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-file-blank"></i> Draft',
|
||||
value: (rowData) => rowData[3].includes("Draft"),
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-globe"></i> Online',
|
||||
value: (rowData) => rowData[3].includes("Online"),
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-file-blank"></i> Draft',
|
||||
value: (rowData) => rowData[3].includes("Draft"),
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 3,
|
||||
},
|
||||
targets: 3,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 4,
|
||||
},
|
||||
targets: 4,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Last 24 hours",
|
||||
value: (rowData) => new Date() - new Date(rowData[5]) < 86400000,
|
||||
},
|
||||
{
|
||||
label: "Last 7 days",
|
||||
value: (rowData) => new Date() - new Date(rowData[5]) < 604800000,
|
||||
},
|
||||
{
|
||||
label: "Last 30 days",
|
||||
value: (rowData) =>
|
||||
new Date() - new Date(rowData[5]) < 2592000000,
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Last 24 hours",
|
||||
value: (rowData) =>
|
||||
new Date() - new Date(rowData[5]) < 86400000,
|
||||
},
|
||||
{
|
||||
label: "Last 7 days",
|
||||
value: (rowData) =>
|
||||
new Date() - new Date(rowData[5]) < 604800000,
|
||||
},
|
||||
{
|
||||
label: "Last 30 days",
|
||||
value: (rowData) =>
|
||||
new Date() - new Date(rowData[5]) < 2592000000,
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 5,
|
||||
},
|
||||
targets: 5,
|
||||
},
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Last 24 hours",
|
||||
value: (rowData) => new Date() - new Date(rowData[6]) < 86400000,
|
||||
},
|
||||
{
|
||||
label: "Last 7 days",
|
||||
value: (rowData) => new Date() - new Date(rowData[6]) < 604800000,
|
||||
},
|
||||
{
|
||||
label: "Last 30 days",
|
||||
value: (rowData) =>
|
||||
new Date() - new Date(rowData[6]) < 2592000000,
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
{
|
||||
searchPanes: {
|
||||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: "Last 24 hours",
|
||||
value: (rowData) =>
|
||||
new Date() - new Date(rowData[6]) < 86400000,
|
||||
},
|
||||
{
|
||||
label: "Last 7 days",
|
||||
value: (rowData) =>
|
||||
new Date() - new Date(rowData[6]) < 604800000,
|
||||
},
|
||||
{
|
||||
label: "Last 30 days",
|
||||
value: (rowData) =>
|
||||
new Date() - new Date(rowData[6]) < 2592000000,
|
||||
},
|
||||
],
|
||||
combiner: "or",
|
||||
orderable: false,
|
||||
},
|
||||
targets: 6,
|
||||
},
|
||||
targets: 6,
|
||||
},
|
||||
],
|
||||
order: [[2, "asc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
select: {
|
||||
style: "multi+shift",
|
||||
selector: "td:nth-child(2)",
|
||||
headerCheckbox: true,
|
||||
},
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ services",
|
||||
infoEmpty: "No services available",
|
||||
infoFiltered: "(filtered from _MAX_ total services)",
|
||||
lengthMenu: "Display _MENU_ services",
|
||||
zeroRecords: "No matching services found",
|
||||
],
|
||||
order: [[2, "asc"]],
|
||||
autoFill: false,
|
||||
responsive: true,
|
||||
select: {
|
||||
rows: {
|
||||
_: "Selected %d services",
|
||||
0: "No services selected",
|
||||
1: "Selected 1 service",
|
||||
style: "multi+shift",
|
||||
selector: "td:nth-child(2)",
|
||||
headerCheckbox: true,
|
||||
},
|
||||
layout: layout,
|
||||
language: {
|
||||
info: "Showing _START_ to _END_ of _TOTAL_ services",
|
||||
infoEmpty: "No services available",
|
||||
infoFiltered: "(filtered from _MAX_ total services)",
|
||||
lengthMenu: "Display _MENU_ services",
|
||||
zeroRecords: "No matching services found",
|
||||
select: {
|
||||
rows: {
|
||||
_: "Selected %d services",
|
||||
0: "No services selected",
|
||||
1: "Selected 1 service",
|
||||
},
|
||||
},
|
||||
searchPanes: {
|
||||
collapse: {
|
||||
0: '<span class="tf-icons bx bx-search bx-18px me-2"></span>Filters',
|
||||
_: '<span class="tf-icons bx bx-search bx-18px me-2"></span>Filters (%d)',
|
||||
},
|
||||
},
|
||||
},
|
||||
searchPanes: {
|
||||
collapse: {
|
||||
0: '<span class="tf-icons bx bx-search bx-18px me-2"></span>Filters',
|
||||
_: '<span class="tf-icons bx bx-search bx-18px me-2"></span>Filters (%d)',
|
||||
},
|
||||
},
|
||||
},
|
||||
initComplete: function () {
|
||||
const $wrapper = $("#services_wrapper");
|
||||
$wrapper.find(".btn-secondary").removeClass("btn-secondary");
|
||||
if (isReadOnly) {
|
||||
$wrapper
|
||||
.find(".dt-buttons")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"The database is in read-only mode; you cannot create new services.",
|
||||
)
|
||||
.attr("data-bs-placement", "right")
|
||||
.tooltip();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
services_table.searchPanes.container().hide();
|
||||
|
||||
$(".action-button")
|
||||
.parent()
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
|
||||
$("#services").removeClass("d-none");
|
||||
$("#services-waiting").addClass("visually-hidden");
|
||||
|
||||
const defaultColsVisibility = JSON.parse(
|
||||
$("#columns_preferences_defaults").val().trim(),
|
||||
);
|
||||
|
||||
var columnVisibility = localStorage.getItem("bw-services-columns");
|
||||
if (columnVisibility === null) {
|
||||
columnVisibility = JSON.parse($("#columns_preferences").val().trim());
|
||||
} else {
|
||||
columnVisibility = JSON.parse(columnVisibility);
|
||||
}
|
||||
|
||||
Object.entries(columnVisibility).forEach(([key, value]) => {
|
||||
services_table.column(key).visible(value);
|
||||
});
|
||||
|
||||
services_table.responsive.recalc();
|
||||
|
||||
services_table.on("mouseenter", "td", function () {
|
||||
if (services_table.cell(this).index() === undefined) return;
|
||||
const rowIdx = services_table.cell(this).index().row;
|
||||
|
||||
services_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
|
||||
services_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each(function (el) {
|
||||
if (services_table.cell(el).index().row === rowIdx)
|
||||
el.classList.add("highlight");
|
||||
});
|
||||
});
|
||||
|
||||
services_table.on("mouseleave", "td", function () {
|
||||
services_table
|
||||
.cells()
|
||||
.nodes()
|
||||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
services_table.on("select", function (e, dt, type, indexes) {
|
||||
// Enable the actions button
|
||||
$(".action-button")
|
||||
.removeClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", null)
|
||||
.attr("data-bs-original-title", null)
|
||||
.attr("data-bs-placement", null)
|
||||
.tooltip("dispose");
|
||||
});
|
||||
|
||||
services_table.on("deselect", function (e, dt, type, indexes) {
|
||||
// If no rows are selected, disable the actions button
|
||||
if (services_table.rows({ selected: true }).count() === 0) {
|
||||
$(".action-button")
|
||||
.addClass("disabled")
|
||||
.parent()
|
||||
.attr("data-bs-toggle", "tooltip")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"Please select one or more rows to perform an action.",
|
||||
)
|
||||
.attr("data-bs-placement", "top")
|
||||
.tooltip();
|
||||
}
|
||||
});
|
||||
|
||||
const debounce = (func, delay) => {
|
||||
let timer = null;
|
||||
|
||||
return (...args) => {
|
||||
// Clear the timer if the function is called again during the delay
|
||||
if (timer) clearTimeout(timer);
|
||||
|
||||
// Start a new timer to invoke the function after the delay
|
||||
timer = setTimeout(() => {
|
||||
func(...args);
|
||||
}, delay);
|
||||
};
|
||||
};
|
||||
|
||||
const saveColumnsPreferences = debounce(() => {
|
||||
const rootUrl = $("#home-path")
|
||||
.val()
|
||||
.trim()
|
||||
.replace(/\/home$/, "/set_columns_preferences");
|
||||
const csrfToken = $("#csrf_token").val();
|
||||
|
||||
const data = new FormData();
|
||||
data.append("csrf_token", csrfToken);
|
||||
data.append("table_name", "services");
|
||||
data.append("columns_preferences", JSON.stringify(columnVisibility));
|
||||
|
||||
fetch(rootUrl, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
initComplete: function () {
|
||||
const $wrapper = $("#services_wrapper");
|
||||
$wrapper.find(".btn-secondary").removeClass("btn-secondary");
|
||||
if (isReadOnly) {
|
||||
$wrapper
|
||||
.find(".dt-buttons")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
"The database is in read-only mode; you cannot create new services.",
|
||||
)
|
||||
.attr("data-bs-placement", "right")
|
||||
.tooltip();
|
||||
}
|
||||
console.log("Preferences saved successfully!");
|
||||
// Handle success, redirect, etc.
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("There was a problem with the fetch operation:", error);
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
services_table.on(
|
||||
"column-visibility.dt",
|
||||
function (e, settings, column, state) {
|
||||
if (column === 0 || column === 1 || column === 7) return;
|
||||
columnVisibility[column] = state;
|
||||
// Check if columVisibility is equal to defaultColsVisibility
|
||||
const isDefault =
|
||||
JSON.stringify(columnVisibility) ===
|
||||
JSON.stringify(defaultColsVisibility);
|
||||
// If it is, remove the key from localStorage
|
||||
if (isDefault) {
|
||||
localStorage.removeItem("bw-services-columns");
|
||||
} else {
|
||||
localStorage.setItem(
|
||||
"bw-services-columns",
|
||||
JSON.stringify(columnVisibility),
|
||||
);
|
||||
}
|
||||
|
||||
saveColumnsPreferences();
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
$(document).on("click", ".delete-service", function () {
|
||||
if (isReadOnly) {
|
||||
|
|
|
|||
|
|
@ -86,21 +86,6 @@ $(document).ready(() => {
|
|||
.toggleClass("is-invalid", !isValid);
|
||||
};
|
||||
|
||||
/**
|
||||
* Debounce function to limit the rate at which a function can fire.
|
||||
* @param {Function} func - The function to debounce.
|
||||
* @param {number} delay - The delay in milliseconds.
|
||||
* @returns {Function} Debounced function.
|
||||
*/
|
||||
const debounce = (func, delay) => {
|
||||
let debounceTimer;
|
||||
return function (...args) {
|
||||
const context = this;
|
||||
clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(() => func.apply(context, args), delay);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts and encodes the server name from the input.
|
||||
* @returns {string} Encoded server name.
|
||||
|
|
|
|||
|
|
@ -414,14 +414,6 @@ $(document).ready(() => {
|
|||
return form;
|
||||
};
|
||||
|
||||
const debounce = (func, delay) => {
|
||||
let debounceTimer;
|
||||
return (...args) => {
|
||||
clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(() => func.apply(this, args), delay);
|
||||
};
|
||||
};
|
||||
|
||||
$("#select-plugin").on("click", () => $pluginSearch.focus());
|
||||
|
||||
$pluginSearch.on(
|
||||
|
|
|
|||
|
|
@ -423,20 +423,6 @@ $(document).ready(() => {
|
|||
updateNotificationsBadge();
|
||||
});
|
||||
|
||||
const debounce = (func, delay) => {
|
||||
let timer = null;
|
||||
|
||||
return (...args) => {
|
||||
// Clear the timer if the function is called again during the delay
|
||||
if (timer) clearTimeout(timer);
|
||||
|
||||
// Start a new timer to invoke the function after the delay
|
||||
timer = setTimeout(() => {
|
||||
func(...args);
|
||||
}, delay);
|
||||
};
|
||||
};
|
||||
|
||||
const saveTheme = debounce((rootUrl, theme) => {
|
||||
const csrfToken = $("#csrf_token").val();
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -160,6 +160,8 @@
|
|||
nonce="{{ script_nonce }}"></script>
|
||||
<script src="{{ url_for('static', filename='libs/datatables/plugins/ip-address.js') }}"
|
||||
nonce="{{ script_nonce }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/dataTableInit.js') }}"
|
||||
nonce="{{ script_nonce }}"></script>
|
||||
{% endif %}
|
||||
{% if current_endpoint == "home" or current_endpoint != "plugins" and "plugins" in request.path %}
|
||||
<script src="{{ url_for('static', filename='libs/apexcharts/apexcharts.min.js') }}"
|
||||
|
|
@ -187,6 +189,8 @@
|
|||
<!-- Main JS -->
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}"
|
||||
nonce="{{ script_nonce }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/common.js') }}"
|
||||
nonce="{{ script_nonce }}"></script>
|
||||
{% if current_endpoint in ("setup", "login", "totp", "loading") %}
|
||||
<script src="{{ url_for('static', filename='js/pages/login.js') }}"
|
||||
nonce="{{ script_nonce }}"></script>
|
||||
|
|
|
|||
Loading…
Reference in a new issue