240 lines
8.8 KiB
Django/Jinja
240 lines
8.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;
|
|
}
|
|
|
|
.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="fas fa-list-alt"></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-2">
|
|
<label for="categoryFilter" class="form-label">Category:</label>
|
|
<select id="categoryFilter" class="form-select form-select-sm">
|
|
<option value="">All Categories</option>
|
|
{% for cat in categories %}
|
|
<option value="{{ cat }}" {% if category==cat %}selected{% endif %}>{{ cat.replace('_', '
|
|
').title() }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-md-2">
|
|
<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 class="col-md-2">
|
|
<label for="startDate" class="form-label">Start Date:</label>
|
|
<input type="date" id="startDate" class="form-control form-control-sm"
|
|
value="{{ start_date or '' }}">
|
|
</div>
|
|
|
|
<div class="col-md-2">
|
|
<label for="endDate" class="form-label">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 action or description..." value="{{ search_term or '' }}">
|
|
</div>
|
|
|
|
<div class="col-md-1">
|
|
<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">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
</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="fas fa-sync-alt"></i> Refresh
|
|
</button>
|
|
<button type="button" id="downloadLogs" class="btn btn-outline-success btn-sm">
|
|
<i class="fas fa-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>
|
|
<th style="width: 60px;">Details</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="logsTableBody">
|
|
<tr>
|
|
<td colspan="6" class="text-center py-4">
|
|
<i class="fas fa-spinner fa-spin"></i> 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({
|
|
initialCategory: "{{ category or '' }}",
|
|
initialStartDate: "{{ start_date or '' }}",
|
|
initialEndDate: "{{ end_date or '' }}",
|
|
initialSearchTerm: "{{ 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 %} |