mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-23 16:05:09 +00:00
383 lines
24 KiB
HTML
383 lines
24 KiB
HTML
<!DOCTYPE html>
|
|
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
|
|
<head>
|
|
<th:block th:insert="~{fragments/common :: head(title='Audit Dashboard', header='Audit Dashboard')}"></th:block>
|
|
|
|
<!-- Include Chart.js for visualizations -->
|
|
<script th:src="@{/js/thirdParty/chart.umd.min.js}"></script>
|
|
|
|
<!-- Include custom CSS -->
|
|
<link rel="stylesheet" th:href="@{/css/audit-dashboard.css}" />
|
|
</head>
|
|
<body>
|
|
<div id="page-container">
|
|
<div id="content-wrap">
|
|
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
|
|
|
|
<div class="container-fluid mt-4">
|
|
<h1 class="mb-4" th:text="#{audit.dashboard.title}">Audit Dashboard</h1>
|
|
|
|
<!-- System Status Card -->
|
|
<div class="card dashboard-card mb-4">
|
|
<div class="card-header">
|
|
<h2 class="h5 mb-0" th:text="#{audit.dashboard.systemStatus}">Audit System Status</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="stat-card">
|
|
<div class="stat-label" th:text="#{audit.dashboard.status}">Status</div>
|
|
<div class="stat-number">
|
|
<span th:if="${auditEnabled}" class="text-success" th:text="#{audit.dashboard.enabled}">Enabled</span>
|
|
<span th:unless="${auditEnabled}" class="text-danger" th:text="#{audit.dashboard.disabled}">Disabled</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="stat-card">
|
|
<div class="stat-label" th:text="#{audit.dashboard.currentLevel}">Current Level</div>
|
|
<div class="stat-number">
|
|
<span th:class="'level-indicator level-' + ${auditLevelInt}" th:text="${auditLevel}">STANDARD</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="stat-card">
|
|
<div class="stat-label" th:text="#{audit.dashboard.retentionPeriod}">Retention Period</div>
|
|
<div class="stat-number" th:text="${retentionDays} + ' ' + #{audit.dashboard.days}">90 days</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="stat-card">
|
|
<div class="stat-label" th:text="#{audit.dashboard.totalEvents}">Total Events</div>
|
|
<div class="stat-number" id="total-events">-</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tabs for different sections -->
|
|
<ul class="nav nav-tabs" id="auditTabs" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" id="dashboard-tab" data-bs-toggle="tab" data-bs-target="#dashboard" type="button" role="tab" aria-controls="dashboard" aria-selected="true" th:text="#{audit.dashboard.tab.dashboard}">Dashboard</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="events-tab" data-bs-toggle="tab" data-bs-target="#events" type="button" role="tab" aria-controls="events" aria-selected="false" th:text="#{audit.dashboard.tab.events}">Audit Events</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="export-tab" data-bs-toggle="tab" data-bs-target="#export" type="button" role="tab" aria-controls="export" aria-selected="false" th:text="#{audit.dashboard.tab.export}">Export</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="tab-content" id="auditTabsContent">
|
|
<!-- Dashboard Tab -->
|
|
<div class="tab-pane fade show active" id="dashboard" role="tabpanel" aria-labelledby="dashboard-tab">
|
|
<div class="row mt-4">
|
|
<div class="col-md-6">
|
|
<div class="card dashboard-card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h3 class="h5 mb-0" th:text="#{audit.dashboard.eventsByType}">Events by Type</h3>
|
|
<div class="btn-group">
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="loadStats(7)" th:text="#{audit.dashboard.period.7days}">7 Days</button>
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="loadStats(30)" th:text="#{audit.dashboard.period.30days}">30 Days</button>
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="loadStats(90)" th:text="#{audit.dashboard.period.90days}">90 Days</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="chart-container position-relative">
|
|
<div class="loading-overlay" id="type-chart-loading">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden" th:text="#{loading}">Loading...</span>
|
|
</div>
|
|
</div>
|
|
<canvas id="typeChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="card dashboard-card">
|
|
<div class="card-header">
|
|
<h3 class="h5 mb-0" th:text="#{audit.dashboard.eventsByUser}">Events by User</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="chart-container position-relative">
|
|
<div class="loading-overlay" id="user-chart-loading">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden" th:text="#{loading}">Loading...</span>
|
|
</div>
|
|
</div>
|
|
<canvas id="userChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row mt-4">
|
|
<div class="col-12">
|
|
<div class="card dashboard-card">
|
|
<div class="card-header">
|
|
<h3 class="h5 mb-0" th:text="#{audit.dashboard.eventsOverTime}">Events Over Time</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="chart-container position-relative">
|
|
<div class="loading-overlay" id="time-chart-loading">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden" th:text="#{loading}">Loading...</span>
|
|
</div>
|
|
</div>
|
|
<canvas id="timeChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Events Tab -->
|
|
<div class="tab-pane fade" id="events" role="tabpanel" aria-labelledby="events-tab">
|
|
<div class="card dashboard-card mt-4">
|
|
<div class="card-header">
|
|
<h3 class="h5 mb-0" th:text="#{audit.dashboard.auditEvents}">Audit Events</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Filters -->
|
|
<div class="card filter-card">
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="mb-3">
|
|
<label for="typeFilter" class="form-label" th:text="#{audit.dashboard.filter.eventType}">Event Type</label>
|
|
<select class="form-select" id="typeFilter">
|
|
<option value="" th:text="#{audit.dashboard.filter.allEventTypes}">All event types</option>
|
|
<!-- Will be populated from API -->
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="mb-3">
|
|
<label for="principalFilter" class="form-label" th:text="#{audit.dashboard.filter.user}">User</label>
|
|
<input type="text" class="form-control" id="principalFilter" th:placeholder="#{audit.dashboard.filter.userPlaceholder}" placeholder="Filter by user">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="mb-3">
|
|
<label for="startDateFilter" class="form-label" th:text="#{audit.dashboard.filter.startDate}">Start Date</label>
|
|
<input type="date" class="form-control" id="startDateFilter">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="mb-3">
|
|
<label for="endDateFilter" class="form-label" th:text="#{audit.dashboard.filter.endDate}">End Date</label>
|
|
<input type="date" class="form-control" id="endDateFilter">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<button id="applyFilters" class="btn btn-primary" th:text="#{audit.dashboard.filter.apply}">Apply Filters</button>
|
|
<button id="resetFilters" class="btn btn-secondary" th:text="#{reset}">Reset</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Event Table -->
|
|
<div class="table-responsive position-relative">
|
|
<div class="loading-overlay" id="table-loading">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden" th:text="#{loading}">Loading...</span>
|
|
</div>
|
|
</div>
|
|
<table class="table table-striped table-hover audit-table">
|
|
<thead>
|
|
<tr>
|
|
<th th:text="#{audit.dashboard.table.id}">ID</th>
|
|
<th th:text="#{audit.dashboard.table.time}">Time</th>
|
|
<th th:text="#{audit.dashboard.table.user}">User</th>
|
|
<th th:text="#{audit.dashboard.table.type}">Type</th>
|
|
<th th:text="#{audit.dashboard.table.details}">Details</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="auditTableBody">
|
|
<!-- Table rows will be populated by JavaScript -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
<div class="pagination-container">
|
|
<div>
|
|
<span th:text="#{audit.dashboard.pagination.show}">Show</span>
|
|
<select id="pageSizeSelect" class="form-select form-select-sm d-inline-block w-auto mx-2">
|
|
<option value="10">10</option>
|
|
<option value="20" selected>20</option>
|
|
<option value="50">50</option>
|
|
<option value="100">100</option>
|
|
</select>
|
|
<span th:text="#{audit.dashboard.pagination.entries}">entries</span>
|
|
<span class="mx-3" th:text="#{audit.dashboard.pagination.pageInfo1}">Page </span><span id="currentPage">1</span> <span th:text="#{audit.dashboard.pagination.pageInfo2}">of</span> <span id="totalPages">1</span> (<span th:text="#{audit.dashboard.pagination.totalRecords}">Total records:</span> <span id="totalRecords">0</span>)
|
|
</div>
|
|
<nav aria-label="Audit events pagination">
|
|
<div class="btn-group" role="group" aria-label="Pagination">
|
|
<button type="button" class="btn btn-outline-primary" id="page-first">«</button>
|
|
<button type="button" class="btn btn-outline-primary" id="page-prev">‹</button>
|
|
<span class="btn btn-outline-secondary disabled" id="page-indicator">Page 1 of 1</span>
|
|
<button type="button" class="btn btn-outline-primary" id="page-next">›</button>
|
|
<button type="button" class="btn btn-outline-primary" id="page-last">»</button>
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Event Details Modal -->
|
|
<div class="modal fade" id="eventDetailsModal" tabindex="-1" aria-labelledby="eventDetailsModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="eventDetailsModalLabel" th:text="#{audit.dashboard.modal.eventDetails}">Event Details</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" th:aria-label="#{close}" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<strong th:text="#{audit.dashboard.modal.id} + ':'">ID:</strong> <span id="modal-id"></span>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<strong th:text="#{audit.dashboard.modal.user} + ':'">User:</strong> <span id="modal-principal"></span>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<strong th:text="#{audit.dashboard.modal.type} + ':'">Type:</strong> <span id="modal-type"></span>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-12">
|
|
<strong th:text="#{audit.dashboard.modal.time} + ':'">Time:</strong> <span id="modal-timestamp"></span>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<strong th:text="#{audit.dashboard.modal.data} + ':'">Data:</strong>
|
|
<div class="json-viewer" id="modal-data"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" th:text="#{close}">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Export Tab -->
|
|
<div class="tab-pane fade" id="export" role="tabpanel" aria-labelledby="export-tab">
|
|
<div class="card dashboard-card mt-4">
|
|
<div class="card-header">
|
|
<h3 class="h5 mb-0" th:text="#{audit.dashboard.export.title}">Export Audit Data</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Export Filters -->
|
|
<div class="card filter-card">
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="mb-3">
|
|
<label for="exportTypeFilter" class="form-label" th:text="#{audit.dashboard.filter.eventType}">Event Type</label>
|
|
<select class="form-select" id="exportTypeFilter">
|
|
<option value="" th:text="#{audit.dashboard.filter.allEventTypes}">All event types</option>
|
|
<!-- Will be populated from API -->
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="mb-3">
|
|
<label for="exportPrincipalFilter" class="form-label" th:text="#{audit.dashboard.filter.user}">User</label>
|
|
<input type="text" class="form-control" id="exportPrincipalFilter" th:placeholder="#{audit.dashboard.filter.userPlaceholder}" placeholder="Filter by user">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="mb-3">
|
|
<label for="exportStartDateFilter" class="form-label" th:text="#{audit.dashboard.filter.startDate}">Start Date</label>
|
|
<input type="date" class="form-control" id="exportStartDateFilter">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="mb-3">
|
|
<label for="exportEndDateFilter" class="form-label" th:text="#{audit.dashboard.filter.endDate}">End Date</label>
|
|
<input type="date" class="form-control" id="exportEndDateFilter">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="col-md-6">
|
|
<h5 th:text="#{audit.dashboard.export.format}">Export Format</h5>
|
|
<div>
|
|
<label class="btn btn-outline-primary" style="margin-right: 10px;">
|
|
<input type="radio" name="exportFormat" id="formatCSV" value="csv" checked style="margin-right: 5px;">
|
|
<span th:text="#{audit.dashboard.export.csv}">CSV (Comma Separated Values)</span>
|
|
</label>
|
|
<label class="btn btn-outline-primary">
|
|
<input type="radio" name="exportFormat" id="formatJSON" value="json" style="margin-right: 5px;">
|
|
<span th:text="#{audit.dashboard.export.json}">JSON (JavaScript Object Notation)</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<button id="exportButton" class="btn btn-primary mt-4">
|
|
<i class="bi bi-download"></i> <span th:text="#{audit.dashboard.export.button}">Export Data</span>
|
|
</button>
|
|
<button id="resetExportFilters" class="btn btn-secondary mt-4 ms-2">
|
|
<span th:text="#{audit.dashboard.filter.reset}">Reset Filters</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alert alert-info mt-3">
|
|
<h5 th:text="#{audit.dashboard.export.infoTitle}">Export Information</h5>
|
|
<p th:text="#{audit.dashboard.export.infoDesc1}">The export will include all audit events matching the selected filters. For large datasets, the export may take a few moments to generate.</p>
|
|
<p th:text="#{audit.dashboard.export.infoDesc2}">Exported data will include:</p>
|
|
<ul>
|
|
<li th:text="#{audit.dashboard.export.infoItem1}">Event ID</li>
|
|
<li th:text="#{audit.dashboard.export.infoItem2}">User</li>
|
|
<li th:text="#{audit.dashboard.export.infoItem3}">Event Type</li>
|
|
<li th:text="#{audit.dashboard.export.infoItem4}">Timestamp</li>
|
|
<li th:text="#{audit.dashboard.export.infoItem5}">Event Data</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bootstrap JS is loaded by the common fragments -->
|
|
<script th:src="@{/js/thirdParty/jquery.min.js}"></script>
|
|
<script th:src="@{/js/thirdParty/bootstrap.min.js}"></script>
|
|
|
|
<!-- Internationalization data for JavaScript -->
|
|
<script th:inline="javascript">
|
|
window.i18n = {
|
|
loading: /*[[#{loading}]]*/ 'Loading...',
|
|
noEventsFound: /*[[#{audit.dashboard.js.noEventsFound}]]*/ 'No audit events found matching the current filters',
|
|
errorLoading: /*[[#{audit.dashboard.js.errorLoading}]]*/ 'Error loading data:',
|
|
errorRendering: /*[[#{audit.dashboard.js.errorRendering}]]*/ 'Error rendering table:',
|
|
loadingPage: /*[[#{audit.dashboard.js.loadingPage}]]*/ 'Loading page',
|
|
eventsByType: /*[[#{audit.dashboard.eventsByType}]]*/ 'Events by Type',
|
|
eventsByUser: /*[[#{audit.dashboard.eventsByUser}]]*/ 'Events by User',
|
|
eventsOverTime: /*[[#{audit.dashboard.eventsOverTime}]]*/ 'Events Over Time',
|
|
viewDetails: /*[[#{audit.dashboard.table.viewDetails}]]*/ 'View Details'
|
|
};
|
|
</script>
|
|
|
|
<!-- Load custom JavaScript -->
|
|
<script th:src="@{/js/audit/dashboard.js}"></script>
|
|
</div>
|
|
</div>
|
|
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
|
|
</div>
|
|
</body>
|
|
</html> |