makes logger much more beautiful
This commit is contained in:
parent
4a10052eae
commit
24f9eb5766
@ -15,7 +15,7 @@ def list_logs():
|
|||||||
# The actual data loading will be handled by JavaScript via the API endpoint
|
# The actual data loading will be handled by JavaScript via the API endpoint
|
||||||
|
|
||||||
# Get filter parameters for initial state
|
# 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")
|
start_date = request.args.get("start_date")
|
||||||
end_date = request.args.get("end_date")
|
end_date = request.args.get("end_date")
|
||||||
search_term = request.args.get("search_term")
|
search_term = request.args.get("search_term")
|
||||||
@ -28,7 +28,7 @@ def list_logs():
|
|||||||
return render_template(
|
return render_template(
|
||||||
"logs.html.jinja",
|
"logs.html.jinja",
|
||||||
categories=categories,
|
categories=categories,
|
||||||
category=category,
|
selected_categories=categories_param, # Pass selected categories
|
||||||
start_date=start_date,
|
start_date=start_date,
|
||||||
end_date=end_date,
|
end_date=end_date,
|
||||||
search_term=search_term,
|
search_term=search_term,
|
||||||
@ -39,15 +39,15 @@ def list_logs():
|
|||||||
@bp.route("/download")
|
@bp.route("/download")
|
||||||
def download_logs():
|
def download_logs():
|
||||||
# Filters - reuse logic from list_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")
|
start_date = request.args.get("start_date")
|
||||||
end_date = request.args.get("end_date")
|
end_date = request.args.get("end_date")
|
||||||
search_term = request.args.get("search_term")
|
search_term = request.args.get("search_term")
|
||||||
|
|
||||||
query = ActivityLog.query
|
query = ActivityLog.query
|
||||||
|
|
||||||
if category:
|
if categories:
|
||||||
query = query.filter(ActivityLog.category == category)
|
query = query.filter(ActivityLog.category.in_(categories))
|
||||||
if start_date:
|
if start_date:
|
||||||
start_date_dt = datetime.datetime.strptime(start_date, "%Y-%m-%d")
|
start_date_dt = datetime.datetime.strptime(start_date, "%Y-%m-%d")
|
||||||
query = query.filter(ActivityLog.timestamp >= start_date_dt)
|
query = query.filter(ActivityLog.timestamp >= start_date_dt)
|
||||||
|
@ -27,26 +27,27 @@ class LoggerManager {
|
|||||||
|
|
||||||
initElements() {
|
initElements() {
|
||||||
// Form elements
|
// Form elements
|
||||||
this.filtersForm = document.getElementById("logFiltersForm");
|
this.filtersForm = document.getElementById("filterForm");
|
||||||
this.categorySelect = document.getElementById("category");
|
this.categoryCheckboxes = document.querySelectorAll(".category-checkbox");
|
||||||
this.statusSelect = document.getElementById("status");
|
this.selectAllCategories = document.getElementById("selectAllCategories");
|
||||||
this.startDateInput = document.getElementById("start_date");
|
this.statusSelect = document.getElementById("statusFilter");
|
||||||
this.endDateInput = document.getElementById("end_date");
|
this.startDateInput = document.getElementById("startDate");
|
||||||
this.searchTermInput = document.getElementById("search_term");
|
this.endDateInput = document.getElementById("endDate");
|
||||||
|
this.searchTermInput = document.getElementById("searchTerm");
|
||||||
this.clearFiltersBtn = document.getElementById("clearFilters");
|
this.clearFiltersBtn = document.getElementById("clearFilters");
|
||||||
this.downloadLogsBtn = document.getElementById("downloadLogs");
|
this.downloadLogsBtn = document.getElementById("downloadLogs");
|
||||||
this.refreshLogsBtn = document.getElementById("refreshLogs");
|
this.refreshLogsBtn = document.getElementById("refreshLogs");
|
||||||
|
|
||||||
// Logs display elements
|
// Logs display elements
|
||||||
this.logsTableBody = document.getElementById("logsTableBody");
|
this.logsTableBody = document.getElementById("logsTableBody");
|
||||||
this.pageSizeSelect = document.getElementById("logPageSize");
|
this.pageSizeSelect = document.getElementById("pageSize");
|
||||||
|
|
||||||
// Pagination elements
|
// Pagination elements
|
||||||
this.paginationContainer = document.getElementById("logsPagination");
|
this.paginationContainer = document.getElementById("logsPagination");
|
||||||
this.paginationInfo = document.getElementById("logsPaginationInfo");
|
this.paginationInfo = document.getElementById("paginationDetails");
|
||||||
this.prevPageBtn = document.getElementById("logsPrevPage");
|
this.prevPageBtn = document.getElementById("prevPage");
|
||||||
this.nextPageBtn = document.getElementById("logsNextPage");
|
this.nextPageBtn = document.getElementById("nextPage");
|
||||||
this.currentPageSpan = document.getElementById("logsCurrentPage");
|
this.currentPageSpan = document.getElementById("currentPageSpan");
|
||||||
|
|
||||||
// Modal
|
// Modal
|
||||||
this.logModal = new ModalHandler("logDetailModal", "log-detail-content");
|
this.logModal = new ModalHandler("logDetailModal", "log-detail-content");
|
||||||
@ -61,19 +62,36 @@ class LoggerManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// Individual filter changes for immediate application
|
||||||
[
|
[this.statusSelect, this.startDateInput, this.endDateInput].forEach(
|
||||||
this.categorySelect,
|
(element) => {
|
||||||
this.statusSelect,
|
|
||||||
this.startDateInput,
|
|
||||||
this.endDateInput,
|
|
||||||
].forEach((element) => {
|
|
||||||
if (element) {
|
if (element) {
|
||||||
element.addEventListener("change", () => {
|
element.addEventListener("change", () => {
|
||||||
this.applyFilters();
|
this.applyFilters();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Search term with debounce
|
// Search term with debounce
|
||||||
if (this.searchTermInput) {
|
if (this.searchTermInput) {
|
||||||
@ -139,11 +157,43 @@ class LoggerManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applyInitialFilters() {
|
updateSelectAllState() {
|
||||||
// Set form values from initial filters
|
const checkedCount = Array.from(this.categoryCheckboxes).filter(
|
||||||
if (this.categorySelect && this.initialFilters.category) {
|
(cb) => cb.checked
|
||||||
this.categorySelect.value = this.initialFilters.category;
|
).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) {
|
if (this.startDateInput && this.initialFilters.start_date) {
|
||||||
this.startDateInput.value = this.initialFilters.start_date;
|
this.startDateInput.value = this.initialFilters.start_date;
|
||||||
}
|
}
|
||||||
@ -157,8 +207,10 @@ class LoggerManager {
|
|||||||
|
|
||||||
applyFilters() {
|
applyFilters() {
|
||||||
// Collect current filter values
|
// Collect current filter values
|
||||||
|
const selectedCategories = this.getSelectedCategories();
|
||||||
|
|
||||||
this.filters = {
|
this.filters = {
|
||||||
category: this.categorySelect?.value || "",
|
category: selectedCategories, // Now an array
|
||||||
status: this.statusSelect?.value || "",
|
status: this.statusSelect?.value || "",
|
||||||
start_date: this.startDateInput?.value || "",
|
start_date: this.startDateInput?.value || "",
|
||||||
end_date: this.endDateInput?.value || "",
|
end_date: this.endDateInput?.value || "",
|
||||||
@ -176,8 +228,15 @@ class LoggerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clearAllFilters() {
|
clearAllFilters() {
|
||||||
// Clear all form fields
|
// Clear all category checkboxes and select all
|
||||||
if (this.categorySelect) this.categorySelect.value = "";
|
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.statusSelect) this.statusSelect.value = "";
|
||||||
if (this.startDateInput) this.startDateInput.value = "";
|
if (this.startDateInput) this.startDateInput.value = "";
|
||||||
if (this.endDateInput) this.endDateInput.value = "";
|
if (this.endDateInput) this.endDateInput.value = "";
|
||||||
@ -193,7 +252,7 @@ class LoggerManager {
|
|||||||
try {
|
try {
|
||||||
// Show loading state
|
// Show loading state
|
||||||
this.logsTableBody.innerHTML =
|
this.logsTableBody.innerHTML =
|
||||||
'<tr><td colspan="5" class="text-center">Loading logs...</td></tr>';
|
'<tr><td colspan="5" class="text-center"><div class="spinner-border spinner-border-sm text-primary" role="status"><span class="visually-hidden">Loading...</span></div> Loading logs...</td></tr>';
|
||||||
|
|
||||||
// Build query parameters
|
// Build query parameters
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
@ -204,8 +263,15 @@ class LoggerManager {
|
|||||||
// Add filters to query
|
// Add filters to query
|
||||||
Object.entries(this.filters).forEach(([key, value]) => {
|
Object.entries(this.filters).forEach(([key, value]) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
if (key === "category" && Array.isArray(value)) {
|
||||||
|
// Handle multiple categories
|
||||||
|
value.forEach((cat) => {
|
||||||
|
if (cat) params.append("category", cat);
|
||||||
|
});
|
||||||
|
} else if (value) {
|
||||||
params.append(key, value);
|
params.append(key, value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch logs from unified API
|
// Fetch logs from unified API
|
||||||
@ -239,7 +305,7 @@ class LoggerManager {
|
|||||||
|
|
||||||
logs.forEach((log) => {
|
logs.forEach((log) => {
|
||||||
const row = document.createElement("tr");
|
const row = document.createElement("tr");
|
||||||
row.className = "log-item";
|
row.className = "log-entry";
|
||||||
row.setAttribute("data-log-id", log.id);
|
row.setAttribute("data-log-id", log.id);
|
||||||
|
|
||||||
// Format timestamp
|
// Format timestamp
|
||||||
@ -259,7 +325,7 @@ class LoggerManager {
|
|||||||
<td>${log.description || ""}</td>
|
<td>${log.description || ""}</td>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Add click handler for details modal
|
// Add click handler for details modal - whole row is clickable
|
||||||
row.addEventListener("click", () => {
|
row.addEventListener("click", () => {
|
||||||
const url = `/logs/${log.id}/detail`;
|
const url = `/logs/${log.id}/detail`;
|
||||||
this.logModal.loadAndShow(url, "Error loading log details.");
|
this.logModal.loadAndShow(url, "Error loading log details.");
|
||||||
@ -360,8 +426,15 @@ class LoggerManager {
|
|||||||
|
|
||||||
Object.entries(this.filters).forEach(([key, value]) => {
|
Object.entries(this.filters).forEach(([key, value]) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
if (key === "category" && Array.isArray(value)) {
|
||||||
|
// Handle multiple categories
|
||||||
|
value.forEach((cat) => {
|
||||||
|
if (cat) params.append("category", cat);
|
||||||
|
});
|
||||||
|
} else if (value) {
|
||||||
params.append(key, value);
|
params.append(key, value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const newUrl = `${window.location.pathname}${
|
const newUrl = `${window.location.pathname}${
|
||||||
@ -376,8 +449,15 @@ class LoggerManager {
|
|||||||
|
|
||||||
Object.entries(this.filters).forEach(([key, value]) => {
|
Object.entries(this.filters).forEach(([key, value]) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
if (key === "category" && Array.isArray(value)) {
|
||||||
|
// Handle multiple categories
|
||||||
|
value.forEach((cat) => {
|
||||||
|
if (cat) params.append("category", cat);
|
||||||
|
});
|
||||||
|
} else if (value) {
|
||||||
params.append(key, value);
|
params.append(key, value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const downloadUrl = `/logs/download${
|
const downloadUrl = `/logs/download${
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
<meta name="keywords" content="science, papers, research, management" />
|
<meta name="keywords" content="science, papers, research, management" />
|
||||||
<title>{{ app_title }}</title>
|
<title>{{ app_title }}</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" />
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<!-- Optional Alpine.js -->
|
<!-- Optional Alpine.js -->
|
||||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||||
|
@ -41,6 +41,15 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.log-entry {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-entry:hover {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
.pagination-info {
|
.pagination-info {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
color: #6c757d;
|
color: #6c757d;
|
||||||
@ -70,7 +79,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container-fluid mt-4">
|
<div class="container-fluid mt-4">
|
||||||
<h1><i class="fas fa-list-alt"></i> Activity Logs</h1>
|
<h1><i class="bi bi-list-ul"></i> Activity Logs</h1>
|
||||||
|
|
||||||
<!-- Include standardized flash messages -->
|
<!-- Include standardized flash messages -->
|
||||||
{% include "partials/flash_messages.html.jinja" %}
|
{% include "partials/flash_messages.html.jinja" %}
|
||||||
@ -79,18 +88,34 @@
|
|||||||
<!-- Filter Panel -->
|
<!-- Filter Panel -->
|
||||||
<div class="filter-panel">
|
<div class="filter-panel">
|
||||||
<form id="filterForm" class="row g-3">
|
<form id="filterForm" class="row g-3">
|
||||||
<div class="col-md-2">
|
<div class="col-md-3">
|
||||||
<label for="categoryFilter" class="form-label">Category:</label>
|
<label class="form-label">Categories:</label>
|
||||||
<select id="categoryFilter" class="form-select form-select-sm">
|
<div class="category-checkbox-container p-2"
|
||||||
<option value="">All Categories</option>
|
style="max-height: 200px; overflow-y: auto; background-color: white; border: 1px solid #ced4da; border-radius: 0.375rem;">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="selectAllCategories" {% if not
|
||||||
|
selected_categories or selected_categories|length==categories|length %}checked{% endif
|
||||||
|
%}>
|
||||||
|
<label class="form-check-label fw-bold" for="selectAllCategories">
|
||||||
|
All Categories
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<hr class="my-2">
|
||||||
{% for cat in categories %}
|
{% for cat in categories %}
|
||||||
<option value="{{ cat }}" {% if category==cat %}selected{% endif %}>{{ cat.replace('_', '
|
<div class="form-check">
|
||||||
').title() }}</option>
|
<input class="form-check-input category-checkbox" type="checkbox" id="category_{{ cat }}"
|
||||||
|
value="{{ cat }}" {% if not selected_categories or cat in selected_categories
|
||||||
|
%}checked{% endif %}>
|
||||||
|
<label class="form-check-label" for="category_{{ cat }}">
|
||||||
|
{{ cat.replace('_', ' ').title() }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-2">
|
<div class="col-md-3">
|
||||||
|
<div class="row">
|
||||||
<label for="statusFilter" class="form-label">Status:</label>
|
<label for="statusFilter" class="form-label">Status:</label>
|
||||||
<select id="statusFilter" class="form-select form-select-sm">
|
<select id="statusFilter" class="form-select form-select-sm">
|
||||||
<option value="">All Statuses</option>
|
<option value="">All Statuses</option>
|
||||||
@ -102,34 +127,28 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-2">
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-3">
|
||||||
<label for="startDate" class="form-label">Start Date:</label>
|
<label for="startDate" class="form-label">Start Date:</label>
|
||||||
<input type="date" id="startDate" class="form-control form-control-sm"
|
<input type="date" id="startDate" class="form-control form-control-sm"
|
||||||
value="{{ start_date or '' }}">
|
value="{{ start_date or '' }}">
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-2">
|
<label for="endDate" class="form-label mt-2">End Date:</label>
|
||||||
<label for="endDate" class="form-label">End Date:</label>
|
|
||||||
<input type="date" id="endDate" class="form-control form-control-sm" value="{{ end_date or '' }}">
|
<input type="date" id="endDate" class="form-control form-control-sm" value="{{ end_date or '' }}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label for="searchTerm" class="form-label">Search:</label>
|
<label for="searchTerm" class="form-label">Search:</label>
|
||||||
<input type="text" id="searchTerm" class="form-control form-control-sm"
|
<input type="text" id="searchTerm" class="form-control form-control-sm"
|
||||||
placeholder="Search action or description..." value="{{ search_term or '' }}">
|
placeholder="Search in actions and descriptions" value="{{ search_term or '' }}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-1">
|
<div class="col-12 d-flex justify-content-end mt-3">
|
||||||
<label class="form-label"> </label>
|
|
||||||
<div class="d-flex gap-2">
|
|
||||||
<button type="button" id="applyFilters" class="btn btn-primary btn-sm">
|
|
||||||
<i class="fas fa-filter"></i> Filter
|
|
||||||
</button>
|
|
||||||
<button type="button" id="clearFilters" class="btn btn-outline-secondary btn-sm">
|
<button type="button" id="clearFilters" class="btn btn-outline-secondary btn-sm">
|
||||||
<i class="fas fa-times"></i>
|
<i class="bi bi-x"></i> Clear Filters
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -149,10 +168,10 @@
|
|||||||
|
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<button type="button" id="refreshLogs" class="btn btn-outline-primary btn-sm">
|
<button type="button" id="refreshLogs" class="btn btn-outline-primary btn-sm">
|
||||||
<i class="fas fa-sync-alt"></i> Refresh
|
<i class="bi bi-arrow-clockwise"></i> Refresh
|
||||||
</button>
|
</button>
|
||||||
<button type="button" id="downloadLogs" class="btn btn-outline-success btn-sm">
|
<button type="button" id="downloadLogs" class="btn btn-outline-success btn-sm">
|
||||||
<i class="fas fa-download"></i> Download CSV
|
<i class="bi bi-download"></i> Download CSV
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -167,13 +186,15 @@
|
|||||||
<th style="width: 180px;">Action</th>
|
<th style="width: 180px;">Action</th>
|
||||||
<th style="width: 100px;">Status</th>
|
<th style="width: 100px;">Status</th>
|
||||||
<th>Description</th>
|
<th>Description</th>
|
||||||
<th style="width: 60px;">Details</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="logsTableBody">
|
<tbody id="logsTableBody">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="6" class="text-center py-4">
|
<td colspan="5" class="text-center py-4">
|
||||||
<i class="fas fa-spinner fa-spin"></i> Loading logs...
|
<div class="spinner-border spinner-border-sm text-primary" role="status">
|
||||||
|
<span class="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
|
Loading logs...
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -226,10 +247,12 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
// Initialize the logger manager
|
// Initialize the logger manager
|
||||||
window.loggerManager = new LoggerManager({
|
window.loggerManager = new LoggerManager({
|
||||||
initialCategory: "{{ category or '' }}",
|
initialFilters: {
|
||||||
initialStartDate: "{{ start_date or '' }}",
|
category: {{ selected_categories | tojson }},
|
||||||
initialEndDate: "{{ end_date or '' }}",
|
start_date: "{{ start_date or '' }}",
|
||||||
initialSearchTerm: "{{ search_term or '' }}"
|
end_date: "{{ end_date or '' }}",
|
||||||
|
search_term: "{{ search_term or '' }}"
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set up modal handler for log details
|
// Set up modal handler for log details
|
||||||
|
Loading…
x
Reference in New Issue
Block a user