Refactor searches, env vars fetcher config, urls webpage update

This commit is contained in:
Luciano Gervasoni
2025-04-02 18:45:43 +02:00
parent 077219fcb6
commit 84da104dc8
22 changed files with 676 additions and 1521 deletions

View File

@@ -1,607 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>News</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
function getQueryString(pageNumber, itemsNumber, sources, searches, statuses){
// Query parameters. If input is null, get most recent value
let queryParams = new URLSearchParams(window.location.search);
// page
if (pageNumber == null) pageNumber = queryParams.get("page") ?? 1;
queryParams.set("page", pageNumber);
// items
if (itemsNumber == null) itemsNumber = queryParams.get("items") ?? 15;
queryParams.set("items", itemsNumber);
// sources
if (sources == null) sources = queryParams.get("sources") ?? "all";
queryParams.set("sources", sources);
// searches
if (searches == null) searches = queryParams.get("searches") ?? "all";
queryParams.set("searches", searches);
// status
if (statuses == null) statuses = queryParams.get("status") ?? "all";
queryParams.set("status", statuses);
// Encoding fix: %2C -> ,
let queryParamsString = queryParams.toString();
while (queryParamsString.includes("%2C")) {
queryParamsString = queryParamsString.replace("%2C", ",");
}
return queryParamsString;
}
function loadPage(pageNumber, itemsNumber, sources, searches, statuses) {
$("#item-list").fadeTo(100, 0.5); // Smooth fade effect
$("#loading").show();
queryParamsString = getQueryString(pageNumber, itemsNumber, sources, searches, statuses);
$.ajax({
url: "?" + queryParamsString,
type: "GET",
headers: { "X-Requested-With": "XMLHttpRequest" },
success: function (data) {
$("#item-list").fadeTo(0, 1).html(data.items_html); // Restore opacity smoothly
$("#loading").hide();
// Update URL without reloading
window.history.pushState({}, "", "?" + queryParamsString);
}
});
}
////////////////////////////////////////////////////////////////////////////
// Pagination
////////////////////////////////////////////////////////////////////////////
$(document).on("click", ".pagination a", function (event) {
event.preventDefault();
let page = $(this).attr("data-page");
loadPage(pageNumber=page, itemsNumber=null, sources=null, searches=null, statuses=null);
});
$(document).ready(function () {
////////////////////////////////////////////////////////////////////////////
// Filter updates
////////////////////////////////////////////////////////////////////////////
const sourcesToggleAll = $("#toggle-all-sources");
const sourcesCheckboxes = $(".source-checkbox");
const searchesToggleAll = $("#toggle-all-searches");
const searchesCheckboxes = $(".search-checkbox");
const statusesToggleAll = $("#toggle-all-status");
const statusCheckboxes = $(".status-checkbox");
function updateFilters() {
// Get selected sources
if (sourcesToggleAll.prop("checked")) {
selectedSources = "all";
}
else {
if (sourcesCheckboxes.filter(":checked").length > 0 ){
selectedSources = sourcesCheckboxes.filter(":checked").map(function () {
return $(this).val();
}).get().join(",");
}
else {
selectedSources = "none";
}
}
// Get selected searches
if (searchesToggleAll.prop("checked")) {
selectedSearches = "all";
}
else {
if (searchesCheckboxes.filter(":checked").length > 0 ){
selectedSearches = searchesCheckboxes.filter(":checked").map(function () {
return $(this).val();
}).get().join(",");
}
else {
selectedSearches = "none";
}
}
// Get selected URL statuses
if (statusesToggleAll.prop("checked")) {
selectedStatuses = "all";
}
else {
if (statusCheckboxes.filter(":checked").length > 0 ){
selectedStatuses = statusCheckboxes.filter(":checked").map(function () {
return $(this).val();
}).get().join(",");
}
else {
selectedStatuses = "none";
}
}
// Get selected items per page
let selectedItems = $("input[name='items']:checked").val();
// Update pagination and reload data
loadPage(1, selectedItems, selectedSources, selectedSearches, selectedStatuses);
}
////////////////////////////////////////////////////////////////////////////
// Change triggers
////////////////////////////////////////////////////////////////////////////
// Sources
sourcesToggleAll.on("change", function () {
sourcesCheckboxes.prop("checked", sourcesToggleAll.prop("checked"));
updateFilters();
});
sourcesCheckboxes.on("change", function () {
sourcesToggleAll.prop("checked", sourcesCheckboxes.length === sourcesCheckboxes.filter(":checked").length);
updateFilters();
});
// Searches
searchesToggleAll.on("change", function () {
searchesCheckboxes.prop("checked", searchesToggleAll.prop("checked"));
updateFilters();
});
searchesCheckboxes.on("change", function () {
searchesToggleAll.prop("checked", searchesCheckboxes.length === searchesCheckboxes.filter(":checked").length);
updateFilters();
});
// Status
statusesToggleAll.on("change", function () {
statusCheckboxes.prop("checked", statusesToggleAll.prop("checked"));
updateFilters();
});
statusCheckboxes.on("change", function () {
// If all checkboxes are checked, mark "Toggle All" as checked
statusesToggleAll.prop("checked", statusCheckboxes.length === statusCheckboxes.filter(":checked").length);
updateFilters();
});
// Items change trigger update
$(".items").on("change", updateFilters);
////////////////////////////////////////////////////////////////////////////
// Default values
////////////////////////////////////////////////////////////////////////////
// Sources
sourcesCheckboxes.each(function () { $(this).prop("checked", true); });
sourcesToggleAll.prop("checked", true);
// Searches
searchesCheckboxes.each(function () { $(this).prop("checked", true); });
searchesToggleAll.prop("checked", true);
// Statuses
statusCheckboxes.each(function () { $(this).prop("checked", true); });
statusesToggleAll.prop("checked", true);
// Items
// $("input[name='items'][value='" + 15 + "']").prop("checked", true);
// loadPage(pageNumber=page, itemsNumber=null, sources=null, searches=null, statuses=null);
});
////////////////////////////////////////////////////////////////////////////
// Theme logic
////////////////////////////////////////////////////////////////////////////
function setTheme(mode) {
document.documentElement.setAttribute("data-theme", mode);
document.documentElement.setAttribute("data-bs-theme", mode);
localStorage.setItem("theme", mode);
document.getElementById("theme-icon").innerHTML = mode === "dark" ? "🌞" : "🌙";
document.body.classList.toggle("dark-mode", mode === "dark");
}
function toggleTheme() {
let currentTheme = document.documentElement.getAttribute("data-theme");
setTheme(currentTheme === "dark" ? "light" : "dark");
}
document.addEventListener("DOMContentLoaded", function () {
let savedTheme = localStorage.getItem("theme") ||
(window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
setTheme(savedTheme);
// Local browser timestamp aware for ts_fetch print
document.querySelectorAll(".timestamp").forEach(function (el) {
const ts = el.getAttribute("data-ts");
if (ts) {
const options = {
day: "2-digit",
month: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false // Use 24-hour format
}; // "en-GB" for DD-MM-YYYY
const localDate = new Date(ts).toLocaleString("en-GB", options); // Adjust to browser's timezone
el.innerHTML = `${localDate}`;
}
});
});
////////////////////////////////////////////////////////////////////////////
</script>
<style>
/* Content Area */
#content {
margin-left: 170px; /* Match sidebar width */
min-width: calc(100vw - 170px); /* Ensure it doesn't shrink into the sidebar */
width: calc(100vw - 170px); /* Expands based on screen size */
padding: 20px;
overflow-x: auto; /* Prevent content from being squeezed */
transition: margin-left 0.3s ease;
}
/* Sidebar Styles */
#sidebar {
height: 100vh;
position: fixed;
top: 0;
left: 0;
width: 170px; /* Default width */
background-color: var(--bg-color);
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
padding: 15px;
transition: width 0.3s ease;
/* Enable scrolling */
overflow-y: auto;
max-height: 100vh;
}
#sidebar .nav-link {
color: var(--text-color);
}
#sidebar .nav-link:hover {
background-color: var(--pagination-hover-bg);
}
/* ============================= */
/* Responsive Enhancements */
/* ============================= */
@media (min-width: 1200px) {
.table {
width: 95%; /* Allows table to take more space */
margin: 0 auto; /* Centers the table */
}
}
@media (max-width: 768px) {
#sidebar {
width: 70px; /* Collapse sidebar to smaller width */
/*padding: 10px;*/
}
#content {
margin-left: 70px; /* Adjust margin to match collapsed sidebar */
min-width: calc(100vw - 70px); /* Prevent overlap */
/*padding: 10px;*/
}
/* Adjust table for small screens */
.table-responsive {
overflow-x: auto;
}
.table th,
.table td {
white-space: nowrap; /* Prevent text wrapping in cells */
}
.table a {
word-break: break-word; /* Ensure long URLs break properly */
}
}
/* ============================= */
/* Global Styles */
/* ============================= */
body {
background-color: var(--bg-color);
color: var(--text-color);
transition: background-color 0.3s, color 0.3s;
}
/* ============================= */
/* Light & Dark Mode Variables */
/* ============================= */
:root {
--bg-color: #ffffff;
--text-color: #212529;
--table-bg: #ffffff;
--table-text: #000000;
--table-border: #dee2e6;
--link-color: #007bff;
--pagination-bg: #ffffff;
--pagination-border: #dee2e6;
--pagination-hover-bg: #f8f9fa;
--pagination-active-bg: #007bff;
--pagination-active-text: #ffffff;
--button-bg: #f8f9fa;
--button-border: #ced4da;
--button-text: #212529;
}
[data-theme="dark"] {
--bg-color: #121212;
--text-color: #e0e0e0;
--table-bg: #1e1e1e;
--table-text: #ffffff;
--table-border: #2c2c2c;
--link-color: #9ec5fe;
--pagination-bg: #1e1e1e;
--pagination-border: #444;
--pagination-hover-bg: #333;
--pagination-active-bg: #007bff;
--pagination-active-text: #ffffff;
--button-bg: #1e1e1e;
--button-border: #444;
--button-text: #e0e0e0;
}
/* ============================= */
/* Table Styling */
/* ============================= */
.table-responsive {
width: 100%; /* Ensure it spans the full width of its container */
max-width: 100%;
overflow-x: auto;
}
.table {
background-color: var(--table-bg);
color: var(--table-text);
border: 1px solid var(--table-border);
transition: background-color 0.3s, color 0.3s;
width: 100%; /* Ensures it takes full width of its container */
table-layout: auto; /* Allows columns to adjust dynamically */
/*white-space: nowrap;*/ /* Prevents text wrapping in cells */
}
.table th,
.table td {
border-color: var(--table-border);
}
.table thead {
background-color: var(--pagination-active-bg);
color: var(--pagination-active-text);
}
[data-theme="dark"] .table {
background-color: var(--table-bg);
color: var(--table-text);
}
[data-theme="dark"] .table th,
[data-theme="dark"] .table td {
border-color: var(--table-border);
}
[data-theme="dark"] .table thead {
background-color: #333;
color: #fff;
}
th:nth-child(1), td:nth-child(1) { width: 50%; } /* URL column */
th:nth-child(2), td:nth-child(2) { width: 27.5%; } /* Fetch Date */
th:nth-child(3), td:nth-child(3) { width: 10%; } /* Sources */
th:nth-child(4), td:nth-child(4) { width: 10%; } /* Searches */
th:nth-child(5), td:nth-child(5) { width: 2.5%; } /* Status */
/* ============================= */
/* Pagination Styling */
/* ============================= */
.pagination {
display: flex;
justify-content: center;
padding: 10px 0;
}
.pagination .page-link {
background-color: var(--pagination-bg);
border-color: var(--pagination-border);
color: var(--text-color);
padding: 10px 14px;
margin: 0 5px;
border-radius: 8px;
transition: background-color 0.3s, color 0.3s, transform 0.2s;
}
.pagination .page-link:hover {
background-color: var(--pagination-hover-bg);
transform: scale(1.05);
}
.pagination .active .page-link {
background-color: var(--pagination-active-bg);
color: var(--pagination-active-text);
border-color: var(--pagination-active-bg);
}
/* ============================= */
/* Theme Toggle Button */
/* ============================= */
.theme-toggle-btn {
background-color: var(--button-bg);
border: 1px solid var(--button-border);
color: var(--button-text);
border-radius: 50%;
width: 40px;
height: 40px;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.3s, color 0.3s, transform 0.2s;
cursor: pointer;
}
.theme-toggle-btn:hover {
background-color: var(--pagination-hover-bg);
transform: rotate(20deg);
}
.theme-toggle-btn:active {
transform: scale(0.95);
}
/* ============================= */
/* Loading Spinner Styling */
/* ============================= */
#loading {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 1050;
display: none;
}
.spinner-border {
width: 4rem;
height: 4rem;
}
</style>
</head>
<body>
<!-- Left Sidebar -->
<div id="sidebar" class="d-flex flex-column">
<ul class="nav flex-column">
<!-- Theme Toggle Button -->
<div class="nav-item">
<button onclick="toggleTheme()" class="theme-toggle-btn">
<span id="theme-icon">🌙</span>
</button>
</div>
<!-- URLs per page -->
<div class="nav-item mt-3">
<strong>URLs per page</strong>
<div class="card-body">
<!-- Individual Status Checkboxes -->
{% for url_per_page in list_urls_per_page %}
<div class="items-form-check">
<input class="form-check-input items" type="radio" name="items" id="value-{{ url_per_page }}" value="{{ url_per_page }}">
<label class="form-check-label" for="value-{{ url_per_page }}">{{ url_per_page }}</label>
</div>
{% empty %}
<tr>
<td colspan="2" class="text-center">No options available.</td>
</tr>
{% endfor %}
</div>
</div>
<!-- Status -->
<div class="nav-item mt-3">
<strong>Select status</strong>
<form id="status-filter-form">
<!-- Toggle All Checkbox -->
<div class="status-form-check">
<input class="form-check-input" type="checkbox" id="toggle-all-status">
<label class="form-check-label fw-bold" for="toggle-all-status">
Toggle all
</label>
</div>
<!-- Individual Status Checkboxes -->
{% for status in list_status %}
<div class="status-form-check">
<input class="form-check-input status-checkbox" type="checkbox" value="{{ status }}" id="status-{{ status }}">
<label class="form-check-label" for="status-{{ status }}">
{{ status }}
</label>
</div>
{% empty %}
<tr>
<td colspan="2" class="text-center">No statuses available.</td>
</tr>
{% endfor %}
</form>
</div>
<!-- Sources -->
<div class="nav-item mt-3">
<strong>Select sources</strong>
<form id="source-filter-form">
<!-- Toggle All Checkbox -->
<div class="form-check">
<input class="form-check-input" type="checkbox" id="toggle-all-sources">
<label class="form-check-label fw-bold" for="toggle-all-sources">
Toggle all
</label>
</div>
<!-- Individual Source Checkboxes -->
{% for source in sources %}
<div class="form-check">
<input class="form-check-input source-checkbox" type="checkbox" value="{{ source.id }}" id="source-{{ source.id }}">
<label class="form-check-label" for="source-{{ source.id }}">
{{ source.source }}
</label>
</div>
{% empty %}
<tr>
<td colspan="2" class="text-center">No sources available.</td>
</tr>
{% endfor %}
</form>
</div>
<!-- Searches -->
<div class="nav-item mt-3">
<strong>Select searches</strong>
<form id="search-filter-form">
<!-- Toggle All Checkbox -->
<div class="form-check">
<input class="form-check-input" type="checkbox" id="toggle-all-searches">
<label class="form-check-label fw-bold" for="toggle-all-searches">
Toggle all
</label>
</div>
<!-- Individual Search Checkboxes -->
{% for search in searches %}
<div class="form-check">
<input class="form-check-input search-checkbox" type="checkbox" value="{{ search.id }}" id="search-{{ search.id }}">
<label class="form-check-label" for="search-{{ search.id }}">
[{{ search.type }}] {{ search.search }}
</label>
</div>
{% empty %}
<tr>
<td colspan="2" class="text-center">No search available.</td>
</tr>
{% endfor %}
</form>
</div>
</ul>
</div>
<!-- Main Content Area -->
<div id="content" class="main-content">
<div class="container mt-4">
<!-- Table -->
<div id="item-list">
{% include 'urls_partial.html' %}
</div>
<!-- Loading... -->
<div id="loading" class="text-center mt-3" style="display:none;">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -1,97 +0,0 @@
{% load custom_filters %}
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th scope="col"><strong>URL</strong></th>
<th scope="col"><strong>Fetch date</strong></th>
<th scope="col"><strong>Sources</strong></th>
<th scope="col"><strong>Search</strong></th>
<th scope="col"><strong>Status</strong></th>
</tr>
</thead>
<tbody>
{% for item in page_obj %}
<tr>
<td>
<a href="./{{ item.id }}" class="btn btn-primary btn-sm" target="_blank"></a>
<a href="{{ item.url }}/" target="_blank">{{ item.url }}</a>
</td>
<td class="timestamp" data-ts="{{ item.ts_fetch|date:'c' }}">{{ item.ts_fetch }}</td>
<td>
{% with sources_map|dict_get:item.id as sources %}
{% if sources %}
{% for source in sources %}
<span class="badge bg-secondary">{{ source }}</span>
{% endfor %}
{% else %}
<span class="text-muted">No sources</span>
{% endif %}
{% endwith %}
</td>
<td>
{% with searches_map|dict_get:item.id as searches %}
{% if searches %}
{% for search in searches %}
<span class="badge bg-secondary">{{ search }}</span>
{% endfor %}
{% else %}
<span class="text-muted">No searches</span>
{% endif %}
{% endwith %}
</td>
<td>
{% if item.status == 'raw' %}
<span class="badge bg-secondary">{{ item.status|capfirst }}</span>
{% elif item.status == 'error' %}
<span class="badge bg-danger">{{ item.status|capfirst }}</span>
{% elif item.status == 'valid' %}
<span class="badge bg-success">{{ item.status|capfirst }}</span>
{% elif item.status == 'unknown' %}
<span class="badge bg-warning">{{ item.status|capfirst }}</span>
{% elif item.status == 'invalid' %}
<span class="badge bg-danger">{{ item.status|capfirst }}</span>
{% elif item.status == 'duplicate' %}
<span class="badge bg-info">{{ item.status|capfirst }}</span>
{% else %}
<span class="badge bg-light">Unknown</span>
{% endif %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="4" class="text-center">No items available.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="d-flex justify-content-center mt-3">
<nav>
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="#" data-page="1">First</a>
</li>
<li class="page-item">
<a class="page-link" href="#" data-page="{{ page_obj.previous_page_number }}">Previous</a>
</li>
{% endif %}
<li class="page-item active">
<span class="page-link">Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
</li>
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="#" data-page="{{ page_obj.next_page_number }}">Next</a>
</li>
<li class="page-item">
<a class="page-link" href="#" data-page="{{ page_obj.paginator.num_pages }}">Last</a>
</li>
{% endif %}
</ul>
</nav>
</div>

View File

@@ -258,10 +258,7 @@ input[type="checkbox"] {
<span id="offText" class="off-text">OFF</span>
</span>
</div>
-->
-->
<!-- Pages Per Page Dropdown -->
<h3>Pages Per Page</h3>
@@ -291,28 +288,14 @@ input[type="checkbox"] {
<!-- Filter by Status -->
<h3>Status</h3>
<!--
<label for="toggle-all-checkbox">
<input type="checkbox" id="toggle-all-checkbox" class="toggle-all-checkbox"> Toggle All
</label><br>
{% for status in statuses %}
<label>
<input type="checkbox" name="status" value="{{ status.0 }}"
{% if status.0 in selected_status %}checked{% endif %}
class="status-checkbox">
{{ status.1 }}
</label><br>
{% endfor %}
-->
<button type="button" class="toggle-all-btn" data-toggle="status">Toggle All</button><br>
{% for status in statuses %}
<label>
<input type="checkbox" name="status" value="{{ status.0 }}"
{% if status.0 in selected_status %}checked{% endif %}>
{% if status.0 in selected_status or 'all' in selected_status %}checked{% endif %}>
{{ status.1 }}
</label><br>
{% endfor %}
<!-- Filter by Search -->
<h3>Search</h3>
@@ -320,11 +303,10 @@ input[type="checkbox"] {
{% for search in searches %}
<label>
<input type="checkbox" name="search" value="{{ search.id }}"
{% if search.id|stringformat:"s" in selected_search %}checked{% endif %}>
{% if search.id|stringformat:"s" in selected_search or 'all' in selected_search %}checked{% endif %}>
[{{ search.type }}] {{ search.search|truncatechars:50 }}
</label><br>
{% endfor %}
<!-- Filter by Source -->
<h3>Source</h3>
@@ -332,7 +314,7 @@ input[type="checkbox"] {
{% for source in sources %}
<label>
<input type="checkbox" name="source" value="{{ source.id }}"
{% if source.id|stringformat:"s" in selected_source %}checked{% endif %}>
{% if source.id|stringformat:"s" in selected_source or 'all' in selected_source %}checked{% endif %}>
{{ source.source|truncatechars:50 }}
</label><br>
{% endfor %}
@@ -343,7 +325,7 @@ input[type="checkbox"] {
{% for lang in languages %}
<label>
<input type="checkbox" name="language" value="{{ lang }}"
{% if lang|stringformat:"s" in selected_lang %}checked{% endif %}>
{% if lang|stringformat:"s" in selected_language or 'all' in selected_language%}checked{% endif %}>
{{ lang|truncatechars:50 }}
</label><br>
{% endfor %}
@@ -456,6 +438,7 @@ input[type="checkbox"] {
</div>
<script>
//////////////////////////////////////////////////////////////////////
document.addEventListener("DOMContentLoaded", function () {
//////////////////////////////////////////////
@@ -514,16 +497,40 @@ input[type="checkbox"] {
});
});
//////////////////////////////////////////////////////////////////////
// Function to update the form parameter before submitting
function updateFormParameter(section) {
const checkboxes = document.querySelectorAll(`[name='${section}']`);
const allChecked = Array.from(checkboxes).every(checkbox => checkbox.checked);
// If all are checked, replace them with a hidden input with value "all"
if (allChecked) {
checkboxes.forEach(checkbox => checkbox.removeAttribute("name"));
let hiddenInput = document.createElement("input");
hiddenInput.type = "hidden";
hiddenInput.name = section;
hiddenInput.value = "all";
document.getElementById("filterForm").appendChild(hiddenInput);
} else {
checkboxes.forEach(checkbox => checkbox.setAttribute("name", section));
document.querySelectorAll(`input[name="${section}"][type="hidden"]`).forEach(hiddenInput => hiddenInput.remove());
}
// Submit form after changes
document.getElementById("filterForm").submit();
}
//////////////////////////////////////////////////////////////////////
// Function to toggle all checkboxes in a section
function toggleCheckboxes(section) {
const checkboxes = document.querySelectorAll(`[name='${section}']`);
const allChecked = Array.from(checkboxes).every(checkbox => checkbox.checked);
checkboxes.forEach(checkbox => {
checkbox.checked = !allChecked;
});
checkboxes.forEach(cb => cb.checked = !allChecked);
/*
// Automatically submit the form when a checkbox is toggled
document.getElementById('filterForm').submit();
*/
updateFormParameter(section);
}
// Attach event listeners to "Toggle All" buttons
@@ -533,13 +540,15 @@ input[type="checkbox"] {
toggleCheckboxes(section);
});
});
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Automatically submit the form when any checkbox changes
document.querySelectorAll('input[type="checkbox"]').forEach(function(checkbox) {
checkbox.addEventListener('change', function() {
/*
document.getElementById('filterForm').submit();
*/
updateFormParameter(this.name);
});
});
document.getElementById('perPageSelect').addEventListener('change', function() {
@@ -548,20 +557,6 @@ input[type="checkbox"] {
document.getElementById('timeFilterSelect').addEventListener('change', function() {
document.getElementById('filterForm').submit();
});
/*
document.getElementById('tableRadio').addEventListener('change', function() {
document.getElementById('tableViewContent').classList.remove('d-none');
document.getElementById('chartViewContent').classList.add('d-none');
document.getElementById('filterForm').submit();
});
document.getElementById('chartRadio').addEventListener('change', function() {
document.getElementById('chartViewContent').classList.remove('d-none');
document.getElementById('tableViewContent').classList.add('d-none');
document.getElementById('filterForm').submit();
});
*/
</script>