263 lines
9.8 KiB
Django/Jinja
263 lines
9.8 KiB
Django/Jinja
{% extends "base.html.jinja" %}
|
|
|
|
{% block title %}Activity Logs{% endblock title %}
|
|
|
|
{% block styles %}
|
|
{{ super() }}
|
|
<style>
|
|
.logs-container {
|
|
background: white;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.filter-panel {
|
|
background: #f8f9fa;
|
|
border-bottom: 1px solid #dee2e6;
|
|
padding: 1rem;
|
|
}
|
|
|
|
.log-entry {
|
|
cursor: pointer;
|
|
transition: background-color 0.2s ease;
|
|
}
|
|
|
|
.log-entry:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.category-badge {
|
|
font-size: 0.75rem;
|
|
padding: 0.25rem 0.5rem;
|
|
}
|
|
|
|
.activity-controls {
|
|
width: auto;
|
|
display: inline-block;
|
|
}
|
|
|
|
.logs-table th {
|
|
background-color: #f8f9fa;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.log-entry {
|
|
cursor: pointer;
|
|
transition: background-color 0.2s ease;
|
|
}
|
|
|
|
.log-entry:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.pagination-info {
|
|
font-size: 0.875rem;
|
|
color: #6c757d;
|
|
}
|
|
|
|
.search-results-container {
|
|
max-height: 600px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
/* JSON formatting styles */
|
|
.json-formatted {
|
|
background-color: #f8f9fa;
|
|
border: 1px solid #e9ecef;
|
|
border-radius: 0.375rem;
|
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
font-size: 0.875rem;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.json-formatted code {
|
|
color: #495057;
|
|
background: transparent;
|
|
}
|
|
</style>
|
|
{% endblock styles %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid mt-4">
|
|
<h1><i class="bi bi-list-ul"></i> Activity Logs</h1>
|
|
|
|
<!-- Include standardized flash messages -->
|
|
{% include "partials/flash_messages.html.jinja" %}
|
|
|
|
<div class="logs-container">
|
|
<!-- Filter Panel -->
|
|
<div class="filter-panel">
|
|
<form id="filterForm" class="row g-3">
|
|
<div class="col-md-3">
|
|
<label class="form-label">Categories:</label>
|
|
<div class="category-checkbox-container p-2"
|
|
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 %}
|
|
<div class="form-check">
|
|
<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 %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-3">
|
|
<div class="row">
|
|
<label for="statusFilter" class="form-label">Status:</label>
|
|
<select id="statusFilter" class="form-select form-select-sm">
|
|
<option value="">All Statuses</option>
|
|
<option value="success">Success</option>
|
|
<option value="error">Error</option>
|
|
<option value="warning">Warning</option>
|
|
<option value="info">Info</option>
|
|
<option value="pending">Pending</option>
|
|
</select>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="col-md-3">
|
|
<label for="startDate" class="form-label">Start Date:</label>
|
|
<input type="date" id="startDate" class="form-control form-control-sm"
|
|
value="{{ start_date or '' }}">
|
|
|
|
<label for="endDate" class="form-label mt-2">End Date:</label>
|
|
<input type="date" id="endDate" class="form-control form-control-sm" value="{{ end_date or '' }}">
|
|
</div>
|
|
|
|
<div class="col-md-3">
|
|
<label for="searchTerm" class="form-label">Search:</label>
|
|
<input type="text" id="searchTerm" class="form-control form-control-sm"
|
|
placeholder="Search in actions and descriptions" value="{{ search_term or '' }}">
|
|
</div>
|
|
|
|
<div class="col-12 d-flex justify-content-end mt-3">
|
|
<button type="button" id="clearFilters" class="btn btn-outline-secondary btn-sm">
|
|
<i class="bi bi-x"></i> Clear Filters
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Controls Panel -->
|
|
<div class="d-flex justify-content-between align-items-center p-3 border-bottom">
|
|
<div class="d-flex align-items-center gap-3">
|
|
<div class="form-group mb-0">
|
|
<label for="pageSize" class="form-label mb-0 me-2">Show:</label>
|
|
<select id="pageSize" class="form-select form-select-sm activity-controls">
|
|
<option value="20">20</option>
|
|
<option value="50" selected>50</option>
|
|
<option value="100">100</option>
|
|
</select>
|
|
</div>
|
|
<span id="paginationInfo" class="pagination-info">Loading...</span>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<button type="button" id="refreshLogs" class="btn btn-outline-primary btn-sm">
|
|
<i class="bi bi-arrow-clockwise"></i> Refresh
|
|
</button>
|
|
<button type="button" id="downloadLogs" class="btn btn-outline-success btn-sm">
|
|
<i class="bi bi-download"></i> Download CSV
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Logs Table -->
|
|
<div class="search-results-container">
|
|
<table class="table table-hover logs-table mb-0">
|
|
<thead class="sticky-top">
|
|
<tr>
|
|
<th style="width: 150px;">Timestamp</th>
|
|
<th style="width: 120px;">Category</th>
|
|
<th style="width: 180px;">Action</th>
|
|
<th style="width: 100px;">Status</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="logsTableBody">
|
|
<tr>
|
|
<td colspan="5" class="text-center py-4">
|
|
<div class="spinner-border spinner-border-sm text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
Loading logs...
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Pagination Controls -->
|
|
<nav id="logsPagination" aria-label="Logs pagination" class="p-3 border-top d-none">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div class="pagination-info">
|
|
<span id="paginationDetails">Showing 0 - 0 of 0 entries</span>
|
|
</div>
|
|
<ul class="pagination pagination-sm mb-0">
|
|
<li class="page-item" id="prevPage">
|
|
<a class="page-link" href="#" aria-label="Previous">
|
|
<span aria-hidden="true">«</span>
|
|
</a>
|
|
</li>
|
|
<li class="page-item active" id="currentPageItem">
|
|
<span class="page-link" id="currentPageSpan">1</span>
|
|
</li>
|
|
<li class="page-item" id="nextPage">
|
|
<a class="page-link" href="#" aria-label="Next">
|
|
<span aria-hidden="true">»</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal for log details -->
|
|
<div class="modal fade" id="logDetailModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="true"
|
|
data-bs-keyboard="true">
|
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
|
<div class="modal-content" id="log-detail-content">
|
|
<!-- Log details will be loaded here via AJAX -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock content %}
|
|
|
|
{% block scripts %}
|
|
{{ super() }}
|
|
<script src="{{ url_for('static', filename='js/modal-handler.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='js/logger-manager.js') }}"></script>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
// Initialize the logger manager
|
|
window.loggerManager = new LoggerManager({
|
|
initialFilters: {
|
|
category: {{ selected_categories | tojson }},
|
|
start_date: "{{ start_date or '' }}",
|
|
end_date: "{{ end_date or '' }}",
|
|
search_term: "{{ search_term or '' }}"
|
|
}
|
|
});
|
|
|
|
// Set up modal handler for log details
|
|
const logModal = new ModalHandler('logDetailModal', 'log-detail-content');
|
|
window.loggerManager.setModalHandler(logModal);
|
|
});
|
|
</script>
|
|
{% endblock scripts %} |