refactor: move debounce function to common.js and clean up usage across scripts

This commit is contained in:
Théophile Diot 2024-11-21 12:28:07 +01:00
parent cb62f550ec
commit 3b8ab7c964
No known key found for this signature in database
GPG key ID: FA995104A0BA376A
17 changed files with 1250 additions and 2151 deletions

View 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);
};
}

View 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;
}

View file

@ -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 () {

View file

@ -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),
);
}
});
});

View file

@ -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);

View file

@ -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">&nbsp;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.");

View file

@ -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>&nbsp;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>&nbsp;Up',
value: function (rowData, rowIdx) {
return rowData[5].includes("Up");
},
},
},
{
label:
'<i class="bx bx-xs bx-down-arrow-alt text-danger"></i>&nbsp;Down',
value: function (rowData, rowIdx) {
return rowData[5].includes("Down");
{
label:
'<i class="bx bx-xs bx-down-arrow-alt text-danger"></i>&nbsp;Down',
value: function (rowData, rowIdx) {
return rowData[5].includes("Down");
},
},
},
{
label:
'<i class="bx bx-xs bxs-hourglass text-warning"></i>&nbsp;Loading',
value: function (rowData, rowIdx) {
return rowData[5].includes("Loading");
{
label:
'<i class="bx bx-xs bxs-hourglass text-warning"></i>&nbsp;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>&nbsp;Static',
value: function (rowData, rowIdx) {
return rowData[6].includes("Static");
{
searchPanes: {
show: true,
options: [
{
label: '<i class="bx bx-xs bx-microchip"></i>&nbsp;Static',
value: function (rowData, rowIdx) {
return rowData[6].includes("Static");
},
},
},
{
label: '<i class="bx bx-xs bxl-docker"></i>&nbsp;Container',
value: function (rowData, rowIdx) {
return rowData[6].includes("Container");
{
label: '<i class="bx bx-xs bxl-docker"></i>&nbsp;Container',
value: function (rowData, rowIdx) {
return rowData[6].includes("Container");
},
},
},
{
label: '<i class="bx bx-xs bxl-kubernetes"></i>&nbsp;Pod',
value: function (rowData, rowIdx) {
return rowData[6].includes("Pod");
{
label: '<i class="bx bx-xs bxl-kubernetes"></i>&nbsp;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;

View file

@ -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>&nbsp;No',
value: function (rowData, rowIdx) {
return rowData[5].includes("bx-x");
},
},
{
label: '<i class="bx bx-xs bx-check text-success"></i>&nbsp;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>&nbsp;No',
value: function (rowData, rowIdx) {
return rowData[6].includes("bx-x");
},
},
{
label: '<i class="bx bx-xs bx-check text-success"></i>&nbsp;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>&nbsp;Failed',
value: function (rowData, rowIdx) {
return rowData[7].includes("bx-x");
},
},
{
label:
'<i class="bx bx-xs bx-check text-success"></i>&nbsp;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>&nbsp;No',
value: function (rowData, rowIdx) {
return rowData[5].includes("bx-x");
},
},
{
label:
'<i class="bx bx-xs bx-check text-success"></i>&nbsp;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>&nbsp;No',
value: function (rowData, rowIdx) {
return rowData[6].includes("bx-x");
},
},
{
label:
'<i class="bx bx-xs bx-check text-success"></i>&nbsp;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>&nbsp;Failed',
value: function (rowData, rowIdx) {
return rowData[7].includes("bx-x");
},
},
{
label:
'<i class="bx bx-xs bx-check text-success"></i>&nbsp;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 () {

View file

@ -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");

View file

@ -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">&nbsp;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>&nbsp;No',
value: function (rowData, rowIdx) {
return rowData[6].includes("bx-x");
},
},
{
label: '<i class="bx bx-xs bx-check text-success"></i>&nbsp;Yes',
value: function (rowData, rowIdx) {
return rowData[6].includes("bx-check");
},
},
{
label:
'<i class="bx bx-xs bx-minus text-warning"></i>&nbsp;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">&nbsp;PRO`,
value: function (rowData, rowIdx) {
return rowData[7].includes("PRO");
},
},
{
label: '<i class="bx bx-plug bx-xs"></i>&nbsp;External',
value: function (rowData, rowIdx) {
return rowData[7].includes("EXTERNAL");
},
},
{
label: '<i class="bx bx-cloud-upload bx-xs"></i>&nbsp;UI',
value: function (rowData, rowIdx) {
return rowData[7].includes("UI");
},
},
{
label: '<i class="bx bx-shield bx-xs"></i>&nbsp;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>&nbsp;No',
value: function (rowData, rowIdx) {
return rowData[6].includes("bx-x");
},
},
{
label:
'<i class="bx bx-xs bx-check text-success"></i>&nbsp;Yes',
value: function (rowData, rowIdx) {
return rowData[6].includes("bx-check");
},
},
{
label:
'<i class="bx bx-xs bx-minus text-warning"></i>&nbsp;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">&nbsp;PRO`,
value: function (rowData, rowIdx) {
return rowData[7].includes("PRO");
},
},
{
label: '<i class="bx bx-plug bx-xs"></i>&nbsp;External',
value: function (rowData, rowIdx) {
return rowData[7].includes("EXTERNAL");
},
},
{
label: '<i class="bx bx-cloud-upload bx-xs"></i>&nbsp;UI',
value: function (rowData, rowIdx) {
return rowData[7].includes("UI");
},
},
{
label: '<i class="bx bx-shield bx-xs"></i>&nbsp;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.");

View file

@ -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("#", ""));

View file

@ -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">&nbsp;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>&nbsp;Online',
value: (rowData) => rowData[3].includes("Online"),
},
{
label: '<i class="bx bx-xs bx-file-blank"></i>&nbsp;Draft',
value: (rowData) => rowData[3].includes("Draft"),
},
],
combiner: "or",
orderable: false,
{
searchPanes: {
show: true,
options: [
{
label: '<i class="bx bx-xs bx-globe"></i>&nbsp;Online',
value: (rowData) => rowData[3].includes("Online"),
},
{
label: '<i class="bx bx-xs bx-file-blank"></i>&nbsp;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) {

View file

@ -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.

View file

@ -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(

View file

@ -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

View file

@ -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>