diff --git a/scipaperloader/blueprints/logger.py b/scipaperloader/blueprints/logger.py index 2f8e7ea..fc47b40 100644 --- a/scipaperloader/blueprints/logger.py +++ b/scipaperloader/blueprints/logger.py @@ -15,7 +15,7 @@ def list_logs(): # The actual data loading will be handled by JavaScript via the API endpoint # Get filter parameters for initial state - category = request.args.get("category") + categories_param = request.args.getlist("category") # Get multiple categories start_date = request.args.get("start_date") end_date = request.args.get("end_date") search_term = request.args.get("search_term") @@ -28,7 +28,7 @@ def list_logs(): return render_template( "logs.html.jinja", categories=categories, - category=category, + selected_categories=categories_param, # Pass selected categories start_date=start_date, end_date=end_date, search_term=search_term, @@ -39,15 +39,15 @@ def list_logs(): @bp.route("/download") def download_logs(): # Filters - reuse logic from list_logs - category = request.args.get("category") + categories = request.args.getlist("category") # Get multiple categories start_date = request.args.get("start_date") end_date = request.args.get("end_date") search_term = request.args.get("search_term") query = ActivityLog.query - if category: - query = query.filter(ActivityLog.category == category) + if categories: + query = query.filter(ActivityLog.category.in_(categories)) if start_date: start_date_dt = datetime.datetime.strptime(start_date, "%Y-%m-%d") query = query.filter(ActivityLog.timestamp >= start_date_dt) diff --git a/scipaperloader/static/js/logger-manager.js b/scipaperloader/static/js/logger-manager.js index d32e47a..7744ca0 100644 --- a/scipaperloader/static/js/logger-manager.js +++ b/scipaperloader/static/js/logger-manager.js @@ -27,26 +27,27 @@ class LoggerManager { initElements() { // Form elements - this.filtersForm = document.getElementById("logFiltersForm"); - this.categorySelect = document.getElementById("category"); - this.statusSelect = document.getElementById("status"); - this.startDateInput = document.getElementById("start_date"); - this.endDateInput = document.getElementById("end_date"); - this.searchTermInput = document.getElementById("search_term"); + this.filtersForm = document.getElementById("filterForm"); + this.categoryCheckboxes = document.querySelectorAll(".category-checkbox"); + this.selectAllCategories = document.getElementById("selectAllCategories"); + this.statusSelect = document.getElementById("statusFilter"); + this.startDateInput = document.getElementById("startDate"); + this.endDateInput = document.getElementById("endDate"); + this.searchTermInput = document.getElementById("searchTerm"); this.clearFiltersBtn = document.getElementById("clearFilters"); this.downloadLogsBtn = document.getElementById("downloadLogs"); this.refreshLogsBtn = document.getElementById("refreshLogs"); // Logs display elements this.logsTableBody = document.getElementById("logsTableBody"); - this.pageSizeSelect = document.getElementById("logPageSize"); + this.pageSizeSelect = document.getElementById("pageSize"); // Pagination elements this.paginationContainer = document.getElementById("logsPagination"); - this.paginationInfo = document.getElementById("logsPaginationInfo"); - this.prevPageBtn = document.getElementById("logsPrevPage"); - this.nextPageBtn = document.getElementById("logsNextPage"); - this.currentPageSpan = document.getElementById("logsCurrentPage"); + this.paginationInfo = document.getElementById("paginationDetails"); + this.prevPageBtn = document.getElementById("prevPage"); + this.nextPageBtn = document.getElementById("nextPage"); + this.currentPageSpan = document.getElementById("currentPageSpan"); // Modal this.logModal = new ModalHandler("logDetailModal", "log-detail-content"); @@ -61,20 +62,37 @@ class LoggerManager { }); } - // Individual filter changes for immediate application - [ - this.categorySelect, - this.statusSelect, - this.startDateInput, - this.endDateInput, - ].forEach((element) => { - if (element) { - element.addEventListener("change", () => { - this.applyFilters(); + // Handle "Select All" checkbox for categories + if (this.selectAllCategories) { + this.selectAllCategories.addEventListener("change", () => { + const isChecked = this.selectAllCategories.checked; + this.categoryCheckboxes.forEach((checkbox) => { + checkbox.checked = isChecked; }); - } + this.applyFilters(); + }); + } + + // Handle individual category checkboxes + this.categoryCheckboxes.forEach((checkbox) => { + checkbox.addEventListener("change", () => { + // Update "Select All" checkbox state + this.updateSelectAllState(); + this.applyFilters(); + }); }); + // Individual filter changes for immediate application + [this.statusSelect, this.startDateInput, this.endDateInput].forEach( + (element) => { + if (element) { + element.addEventListener("change", () => { + this.applyFilters(); + }); + } + } + ); + // Search term with debounce if (this.searchTermInput) { let searchTimeout; @@ -139,11 +157,43 @@ class LoggerManager { } } - applyInitialFilters() { - // Set form values from initial filters - if (this.categorySelect && this.initialFilters.category) { - this.categorySelect.value = this.initialFilters.category; + updateSelectAllState() { + const checkedCount = Array.from(this.categoryCheckboxes).filter( + (cb) => cb.checked + ).length; + const totalCount = this.categoryCheckboxes.length; + + if (checkedCount === 0) { + this.selectAllCategories.checked = false; + this.selectAllCategories.indeterminate = false; + } else if (checkedCount === totalCount) { + this.selectAllCategories.checked = true; + this.selectAllCategories.indeterminate = false; + } else { + this.selectAllCategories.checked = false; + this.selectAllCategories.indeterminate = true; } + } + + getSelectedCategories() { + return Array.from(this.categoryCheckboxes) + .filter((checkbox) => checkbox.checked) + .map((checkbox) => checkbox.value); + } + + applyInitialFilters() { + // Set category checkboxes from initial filters + if (this.initialFilters.category) { + const selectedCategories = Array.isArray(this.initialFilters.category) + ? this.initialFilters.category + : [this.initialFilters.category]; + + this.categoryCheckboxes.forEach((checkbox) => { + checkbox.checked = selectedCategories.includes(checkbox.value); + }); + this.updateSelectAllState(); + } + if (this.startDateInput && this.initialFilters.start_date) { this.startDateInput.value = this.initialFilters.start_date; } @@ -157,8 +207,10 @@ class LoggerManager { applyFilters() { // Collect current filter values + const selectedCategories = this.getSelectedCategories(); + this.filters = { - category: this.categorySelect?.value || "", + category: selectedCategories, // Now an array status: this.statusSelect?.value || "", start_date: this.startDateInput?.value || "", end_date: this.endDateInput?.value || "", @@ -176,8 +228,15 @@ class LoggerManager { } clearAllFilters() { - // Clear all form fields - if (this.categorySelect) this.categorySelect.value = ""; + // Clear all category checkboxes and select all + this.categoryCheckboxes.forEach((checkbox) => { + checkbox.checked = true; // Default to all selected + }); + if (this.selectAllCategories) { + this.selectAllCategories.checked = true; + this.selectAllCategories.indeterminate = false; + } + if (this.statusSelect) this.statusSelect.value = ""; if (this.startDateInput) this.startDateInput.value = ""; if (this.endDateInput) this.endDateInput.value = ""; @@ -193,7 +252,7 @@ class LoggerManager { try { // Show loading state this.logsTableBody.innerHTML = - '