mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-23 16:05:09 +00:00
i18n
This commit is contained in:
parent
e0116106c1
commit
43c5a1970f
@ -2,6 +2,19 @@
|
|||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
background-color: var(--md-sys-color-surface-container);
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
border: 1px solid var(--md-sys-color-outline-variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background-color: var(--md-sys-color-surface-container-high);
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
background-color: var(--md-sys-color-surface-container);
|
||||||
}
|
}
|
||||||
.stat-card {
|
.stat-card {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -13,7 +26,7 @@
|
|||||||
}
|
}
|
||||||
.stat-label {
|
.stat-label {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: #666;
|
color: var(--md-sys-color-on-surface-variant);
|
||||||
}
|
}
|
||||||
.chart-container {
|
.chart-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -23,6 +36,9 @@
|
|||||||
.filter-card {
|
.filter-card {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
background-color: var(--md-sys-color-surface-container-low);
|
||||||
|
border: 1px solid var(--md-sys-color-outline-variant);
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
.loading-overlay {
|
.loading-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -30,7 +46,7 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: rgba(255, 255, 255, 0.7);
|
background-color: var(--md-sys-color-surface-container-high, rgba(229, 232, 241, 0.8));
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -44,26 +60,42 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
.level-0 {
|
.level-0 {
|
||||||
background-color: #dc3545; /* Red */
|
background-color: var(--md-sys-color-error, #dc3545); /* Red */
|
||||||
}
|
}
|
||||||
.level-1 {
|
.level-1 {
|
||||||
background-color: #fd7e14; /* Orange */
|
background-color: var(--md-sys-color-secondary, #fd7e14); /* Orange */
|
||||||
}
|
}
|
||||||
.level-2 {
|
.level-2 {
|
||||||
background-color: #28a745; /* Green */
|
background-color: var(--md-nav-section-color-other, #28a745); /* Green */
|
||||||
}
|
}
|
||||||
.level-3 {
|
.level-3 {
|
||||||
background-color: #17a2b8; /* Teal */
|
background-color: var(--md-sys-color-tertiary, #17a2b8); /* Teal */
|
||||||
}
|
}
|
||||||
/* Custom data table styling */
|
/* Custom data table styling */
|
||||||
.audit-table {
|
.audit-table {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
border-color: var(--md-sys-color-outline-variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
.audit-table tbody tr {
|
||||||
|
background-color: var(--md-sys-color-surface-container-low);
|
||||||
|
}
|
||||||
|
|
||||||
|
.audit-table tbody tr:nth-child(even) {
|
||||||
|
background-color: var(--md-sys-color-surface-container);
|
||||||
|
}
|
||||||
|
|
||||||
|
.audit-table tbody tr:hover {
|
||||||
|
background-color: var(--md-sys-color-surface-container-high);
|
||||||
}
|
}
|
||||||
.audit-table th {
|
.audit-table th {
|
||||||
background-color: #f8f9fa;
|
background-color: var(--md-sys-color-surface-container-high);
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
.table-responsive {
|
.table-responsive {
|
||||||
max-height: 600px;
|
max-height: 600px;
|
||||||
@ -74,7 +106,8 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
border-top: 1px solid #dee2e6;
|
border-top: 1px solid var(--md-sys-color-outline-variant);
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination .page-item.active .page-link {
|
.pagination .page-item.active .page-link {
|
||||||
@ -93,13 +126,15 @@
|
|||||||
background-color: var(--bs-light);
|
background-color: var(--bs-light);
|
||||||
}
|
}
|
||||||
.json-viewer {
|
.json-viewer {
|
||||||
background-color: #f8f9fa;
|
background-color: var(--md-sys-color-surface-container-low);
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
border: 1px solid var(--md-sys-color-outline-variant);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Simple, minimal radio styling - no extras */
|
/* Simple, minimal radio styling - no extras */
|
||||||
@ -113,14 +148,14 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
width: 400px;
|
width: 400px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
background: rgba(0,0,0,0.8);
|
background: var(--md-sys-color-surface-container-highest, rgba(0,0,0,0.8));
|
||||||
color: #0f0;
|
color: var(--md-sys-color-tertiary, #0f0);
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid #0f0;
|
border: 1px solid var(--md-sys-color-outline);
|
||||||
display: none; /* Changed to none by default, enable with key command */
|
display: none; /* Changed to none by default, enable with key command */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,13 +163,64 @@
|
|||||||
label.btn-outline-primary {
|
label.btn-outline-primary {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
border-color: var(--md-sys-color-primary);
|
||||||
|
color: var(--md-sys-color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
label.btn-outline-primary.active {
|
label.btn-outline-primary.active {
|
||||||
background-color: var(--bs-primary);
|
background-color: var(--md-sys-color-primary);
|
||||||
color: white;
|
color: var(--md-sys-color-on-primary);
|
||||||
|
border-color: var(--md-sys-color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
label.btn-outline-primary input[type="radio"] {
|
label.btn-outline-primary input[type="radio"] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Modal overrides for dark mode */
|
||||||
|
.modal-content {
|
||||||
|
background-color: var(--md-sys-color-surface-container);
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
border-color: var(--md-sys-color-outline);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
border-bottom-color: var(--md-sys-color-outline-variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
border-top-color: var(--md-sys-color-outline-variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button overrides for theme consistency */
|
||||||
|
.btn-outline-primary {
|
||||||
|
color: var(--md-sys-color-primary);
|
||||||
|
border-color: var(--md-sys-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-primary:hover {
|
||||||
|
background-color: var(--md-sys-color-primary);
|
||||||
|
color: var(--md-sys-color-on-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-secondary {
|
||||||
|
color: var(--md-sys-color-secondary);
|
||||||
|
border-color: var(--md-sys-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-secondary:hover {
|
||||||
|
background-color: var(--md-sys-color-secondary);
|
||||||
|
color: var(--md-sys-color-on-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: var(--md-sys-color-primary);
|
||||||
|
color: var(--md-sys-color-on-primary);
|
||||||
|
border-color: var(--md-sys-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background-color: var(--md-sys-color-secondary);
|
||||||
|
color: var(--md-sys-color-on-secondary);
|
||||||
|
border-color: var(--md-sys-color-secondary);
|
||||||
|
}
|
@ -80,6 +80,31 @@ document.addEventListener('keydown', function(e) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Initialize page
|
// Initialize page
|
||||||
|
// Theme change listener to redraw charts when theme changes
|
||||||
|
function setupThemeChangeListener() {
|
||||||
|
// Watch for theme changes (usually by a class on body or html element)
|
||||||
|
const observer = new MutationObserver(function(mutations) {
|
||||||
|
mutations.forEach(function(mutation) {
|
||||||
|
if (mutation.attributeName === 'data-bs-theme' || mutation.attributeName === 'class') {
|
||||||
|
// Redraw charts with new theme colors if they exist
|
||||||
|
if (typeChart && userChart && timeChart) {
|
||||||
|
debugLog('Theme changed, redrawing charts');
|
||||||
|
// If we have stats data cached, use it
|
||||||
|
if (window.cachedStatsData) {
|
||||||
|
renderCharts(window.cachedStatsData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Observe the document element for theme changes
|
||||||
|
observer.observe(document.documentElement, { attributes: true });
|
||||||
|
|
||||||
|
// Also observe body for class changes
|
||||||
|
observer.observe(document.body, { attributes: true });
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
debugLog('Page initialized');
|
debugLog('Page initialized');
|
||||||
|
|
||||||
@ -106,7 +131,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
// Show a loading message immediately
|
// Show a loading message immediately
|
||||||
if (auditTableBody) {
|
if (auditTableBody) {
|
||||||
auditTableBody.innerHTML =
|
auditTableBody.innerHTML =
|
||||||
'<tr><td colspan="5" class="text-center"><div class="spinner-border spinner-border-sm" role="status"></div> Loading audit data...</td></tr>';
|
'<tr><td colspan="5" class="text-center"><div class="spinner-border spinner-border-sm" role="status"></div> ' + window.i18n.loading + '</td></tr>';
|
||||||
} else {
|
} else {
|
||||||
debugLog('ERROR: auditTableBody element not found!');
|
debugLog('ERROR: auditTableBody element not found!');
|
||||||
}
|
}
|
||||||
@ -117,6 +142,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
// Load statistics for dashboard
|
// Load statistics for dashboard
|
||||||
loadStats(7);
|
loadStats(7);
|
||||||
|
|
||||||
|
// Setup theme change listener
|
||||||
|
setupThemeChangeListener();
|
||||||
|
|
||||||
// Set up event listeners
|
// Set up event listeners
|
||||||
pageSizeSelect.addEventListener('change', function() {
|
pageSizeSelect.addEventListener('change', function() {
|
||||||
pageSize = parseInt(this.value);
|
pageSize = parseInt(this.value);
|
||||||
@ -350,7 +378,7 @@ function loadAuditData(targetPage, realPageSize) {
|
|||||||
.catch(error => {
|
.catch(error => {
|
||||||
debugLog('Error loading data', error.message);
|
debugLog('Error loading data', error.message);
|
||||||
if (auditTableBody) {
|
if (auditTableBody) {
|
||||||
auditTableBody.innerHTML = `<tr><td colspan="5" class="text-center">Error loading data: ${error.message}</td></tr>`;
|
auditTableBody.innerHTML = `<tr><td colspan="5" class="text-center">${window.i18n.errorLoading} ${error.message}</td></tr>`;
|
||||||
}
|
}
|
||||||
hideLoading('table-loading');
|
hideLoading('table-loading');
|
||||||
|
|
||||||
@ -375,6 +403,8 @@ function loadStats(days) {
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
document.getElementById('total-events').textContent = data.totalEvents;
|
document.getElementById('total-events').textContent = data.totalEvents;
|
||||||
|
// Cache stats data for theme changes
|
||||||
|
window.cachedStatsData = data;
|
||||||
renderCharts(data);
|
renderCharts(data);
|
||||||
hideLoading('type-chart-loading');
|
hideLoading('type-chart-loading');
|
||||||
hideLoading('user-chart-loading');
|
hideLoading('user-chart-loading');
|
||||||
@ -412,7 +442,7 @@ function renderTable(events) {
|
|||||||
|
|
||||||
if (!events || events.length === 0) {
|
if (!events || events.length === 0) {
|
||||||
debugLog('No events to render');
|
debugLog('No events to render');
|
||||||
auditTableBody.innerHTML = '<tr><td colspan="5" class="text-center">No audit events found matching the current filters</td></tr>';
|
auditTableBody.innerHTML = '<tr><td colspan="5" class="text-center">' + window.i18n.noEventsFound + '</td></tr>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,7 +482,7 @@ function renderTable(events) {
|
|||||||
debugLog('Table rendering complete');
|
debugLog('Table rendering complete');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugLog('Error in renderTable', e.message);
|
debugLog('Error in renderTable', e.message);
|
||||||
auditTableBody.innerHTML = '<tr><td colspan="5" class="text-center">Error rendering table: ' + e.message + '</td></tr>';
|
auditTableBody.innerHTML = '<tr><td colspan="5" class="text-center">' + window.i18n.errorRendering + ' ' + e.message + '</td></tr>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -521,6 +551,9 @@ function goToPage(page) {
|
|||||||
|
|
||||||
// Render charts
|
// Render charts
|
||||||
function renderCharts(data) {
|
function renderCharts(data) {
|
||||||
|
// Get theme colors
|
||||||
|
const colors = getThemeColors();
|
||||||
|
|
||||||
// Prepare data for charts
|
// Prepare data for charts
|
||||||
const typeLabels = Object.keys(data.eventsByType);
|
const typeLabels = Object.keys(data.eventsByType);
|
||||||
const typeValues = Object.values(data.eventsByType);
|
const typeValues = Object.values(data.eventsByType);
|
||||||
@ -532,6 +565,10 @@ function renderCharts(data) {
|
|||||||
const timeLabels = Object.keys(data.eventsByDay).sort();
|
const timeLabels = Object.keys(data.eventsByDay).sort();
|
||||||
const timeValues = timeLabels.map(day => data.eventsByDay[day] || 0);
|
const timeValues = timeLabels.map(day => data.eventsByDay[day] || 0);
|
||||||
|
|
||||||
|
// Chart.js global defaults for dark mode compatibility
|
||||||
|
Chart.defaults.color = colors.text;
|
||||||
|
Chart.defaults.borderColor = colors.grid;
|
||||||
|
|
||||||
// Type chart
|
// Type chart
|
||||||
if (typeChart) {
|
if (typeChart) {
|
||||||
typeChart.destroy();
|
typeChart.destroy();
|
||||||
@ -543,19 +580,84 @@ function renderCharts(data) {
|
|||||||
data: {
|
data: {
|
||||||
labels: typeLabels,
|
labels: typeLabels,
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'Events by Type',
|
label: window.i18n.eventsByType,
|
||||||
data: typeValues,
|
data: typeValues,
|
||||||
backgroundColor: getChartColors(typeLabels.length),
|
backgroundColor: colors.chartColors.slice(0, typeLabels.length),
|
||||||
borderColor: getChartColors(typeLabels.length, 1), // Full opacity for borders
|
borderColor: colors.chartColors.slice(0, typeLabels.length),
|
||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
labels: {
|
||||||
|
color: colors.text,
|
||||||
|
font: {
|
||||||
|
weight: colors.isDarkMode ? 'bold' : 'normal',
|
||||||
|
size: 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
titleFont: {
|
||||||
|
weight: 'bold',
|
||||||
|
size: 14
|
||||||
|
},
|
||||||
|
bodyFont: {
|
||||||
|
size: 13
|
||||||
|
},
|
||||||
|
backgroundColor: colors.isDarkMode ? 'rgba(0, 0, 0, 0.8)' : 'rgba(255, 255, 255, 0.8)',
|
||||||
|
titleColor: colors.isDarkMode ? '#ffffff' : '#000000',
|
||||||
|
bodyColor: colors.isDarkMode ? '#ffffff' : '#000000',
|
||||||
|
borderColor: colors.grid,
|
||||||
|
borderWidth: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
scales: {
|
scales: {
|
||||||
y: {
|
y: {
|
||||||
beginAtZero: true
|
beginAtZero: true,
|
||||||
|
ticks: {
|
||||||
|
color: colors.text,
|
||||||
|
font: {
|
||||||
|
weight: colors.isDarkMode ? 'bold' : 'normal',
|
||||||
|
size: 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: colors.grid
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Count',
|
||||||
|
color: colors.text,
|
||||||
|
font: {
|
||||||
|
weight: colors.isDarkMode ? 'bold' : 'normal',
|
||||||
|
size: 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
ticks: {
|
||||||
|
color: colors.text,
|
||||||
|
font: {
|
||||||
|
weight: colors.isDarkMode ? 'bold' : 'normal',
|
||||||
|
size: 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: colors.grid
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Event Type',
|
||||||
|
color: colors.text,
|
||||||
|
font: {
|
||||||
|
weight: colors.isDarkMode ? 'bold' : 'normal',
|
||||||
|
size: 14
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -572,15 +674,58 @@ function renderCharts(data) {
|
|||||||
data: {
|
data: {
|
||||||
labels: userLabels,
|
labels: userLabels,
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'Events by User',
|
label: window.i18n.eventsByUser,
|
||||||
data: userValues,
|
data: userValues,
|
||||||
backgroundColor: getChartColors(userLabels.length),
|
backgroundColor: colors.chartColors.slice(0, userLabels.length),
|
||||||
borderWidth: 1
|
borderWidth: 1,
|
||||||
|
borderColor: colors.isDarkMode ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.2)'
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'right',
|
||||||
|
labels: {
|
||||||
|
color: colors.text,
|
||||||
|
font: {
|
||||||
|
size: colors.isDarkMode ? 14 : 12,
|
||||||
|
weight: colors.isDarkMode ? 'bold' : 'normal'
|
||||||
|
},
|
||||||
|
padding: 15,
|
||||||
|
// Add a box around each label for better contrast in dark mode
|
||||||
|
generateLabels: function(chart) {
|
||||||
|
const original = Chart.overrides.pie.plugins.legend.labels.generateLabels;
|
||||||
|
const labels = original.call(this, chart);
|
||||||
|
|
||||||
|
if (colors.isDarkMode) {
|
||||||
|
labels.forEach(label => {
|
||||||
|
label.fillStyle = 'rgba(0, 0, 0, 0.7)'; // Dark background for text
|
||||||
|
label.strokeStyle = label.strokeStyle; // Keep original color for border
|
||||||
|
label.lineWidth = 2; // Thicker border
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return labels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
titleFont: {
|
||||||
|
weight: 'bold',
|
||||||
|
size: 14
|
||||||
|
},
|
||||||
|
bodyFont: {
|
||||||
|
size: 13
|
||||||
|
},
|
||||||
|
backgroundColor: colors.isDarkMode ? 'rgba(0, 0, 0, 0.8)' : 'rgba(255, 255, 255, 0.8)',
|
||||||
|
titleColor: colors.isDarkMode ? '#ffffff' : '#000000',
|
||||||
|
bodyColor: colors.isDarkMode ? '#ffffff' : '#000000',
|
||||||
|
borderColor: colors.grid,
|
||||||
|
borderWidth: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -595,10 +740,10 @@ function renderCharts(data) {
|
|||||||
data: {
|
data: {
|
||||||
labels: timeLabels,
|
labels: timeLabels,
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'Events Over Time',
|
label: window.i18n.eventsOverTime,
|
||||||
data: timeValues,
|
data: timeValues,
|
||||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
backgroundColor: colors.chartColors[0] + '40', // 40 = 25% opacity
|
||||||
borderColor: 'rgba(75, 192, 192, 1)',
|
borderColor: colors.chartColors[0],
|
||||||
tension: 0.1,
|
tension: 0.1,
|
||||||
fill: true
|
fill: true
|
||||||
}]
|
}]
|
||||||
@ -606,9 +751,74 @@ function renderCharts(data) {
|
|||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
labels: {
|
||||||
|
color: colors.text,
|
||||||
|
font: {
|
||||||
|
weight: colors.isDarkMode ? 'bold' : 'normal',
|
||||||
|
size: 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
titleFont: {
|
||||||
|
weight: 'bold',
|
||||||
|
size: 14
|
||||||
|
},
|
||||||
|
bodyFont: {
|
||||||
|
size: 13
|
||||||
|
},
|
||||||
|
backgroundColor: colors.isDarkMode ? 'rgba(0, 0, 0, 0.8)' : 'rgba(255, 255, 255, 0.8)',
|
||||||
|
titleColor: colors.isDarkMode ? '#ffffff' : '#000000',
|
||||||
|
bodyColor: colors.isDarkMode ? '#ffffff' : '#000000',
|
||||||
|
borderColor: colors.grid,
|
||||||
|
borderWidth: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
scales: {
|
scales: {
|
||||||
y: {
|
y: {
|
||||||
beginAtZero: true
|
beginAtZero: true,
|
||||||
|
ticks: {
|
||||||
|
color: colors.text,
|
||||||
|
font: {
|
||||||
|
weight: colors.isDarkMode ? 'bold' : 'normal',
|
||||||
|
size: 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: colors.grid
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Number of Events',
|
||||||
|
color: colors.text,
|
||||||
|
font: {
|
||||||
|
weight: colors.isDarkMode ? 'bold' : 'normal',
|
||||||
|
size: 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
ticks: {
|
||||||
|
color: colors.text,
|
||||||
|
font: {
|
||||||
|
weight: colors.isDarkMode ? 'bold' : 'normal',
|
||||||
|
size: 12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: colors.grid
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Date',
|
||||||
|
color: colors.text,
|
||||||
|
font: {
|
||||||
|
weight: colors.isDarkMode ? 'bold' : 'normal',
|
||||||
|
size: 14
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -684,8 +894,63 @@ function loadEventTypes() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get theme colors for charts
|
||||||
|
function getThemeColors() {
|
||||||
|
const isDarkMode = document.documentElement.getAttribute('data-bs-theme') === 'dark';
|
||||||
|
|
||||||
|
// In dark mode, use higher contrast colors for text
|
||||||
|
const textColor = isDarkMode ?
|
||||||
|
'rgb(255, 255, 255)' : // White for dark mode for maximum contrast
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--md-sys-color-on-surface').trim();
|
||||||
|
|
||||||
|
// Use a more visible grid color in dark mode
|
||||||
|
const gridColor = isDarkMode ?
|
||||||
|
'rgba(255, 255, 255, 0.2)' : // Semi-transparent white for dark mode
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--md-sys-color-outline-variant').trim();
|
||||||
|
|
||||||
|
return {
|
||||||
|
text: textColor,
|
||||||
|
grid: gridColor,
|
||||||
|
backgroundColor: getComputedStyle(document.documentElement).getPropertyValue('--md-sys-color-surface-container').trim(),
|
||||||
|
chartColors: [
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--md-sys-color-primary').trim(),
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--md-sys-color-secondary').trim(),
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--md-sys-color-tertiary').trim(),
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--md-nav-section-color-other').trim(),
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--md-nav-section-color-convert').trim(),
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--md-nav-section-color-sign').trim(),
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--md-nav-section-color-security').trim(),
|
||||||
|
getComputedStyle(document.documentElement).getPropertyValue('--md-nav-section-color-convertto').trim(),
|
||||||
|
],
|
||||||
|
isDarkMode: isDarkMode
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Function to generate a palette of colors for charts
|
// Function to generate a palette of colors for charts
|
||||||
function getChartColors(count, opacity = 0.6) {
|
function getChartColors(count, opacity = 0.6) {
|
||||||
|
try {
|
||||||
|
// Use theme colors first
|
||||||
|
const themeColors = getThemeColors();
|
||||||
|
if (themeColors && themeColors.chartColors && themeColors.chartColors.length > 0) {
|
||||||
|
const result = [];
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
// Get the raw color and add opacity
|
||||||
|
let color = themeColors.chartColors[i % themeColors.chartColors.length];
|
||||||
|
// If it's rgb() format, convert to rgba()
|
||||||
|
if (color.startsWith('rgb(')) {
|
||||||
|
color = color.replace('rgb(', '').replace(')', '');
|
||||||
|
result.push(`rgba(${color}, ${opacity})`);
|
||||||
|
} else {
|
||||||
|
// Just use the color directly
|
||||||
|
result.push(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Error using theme colors, falling back to default colors', e);
|
||||||
|
}
|
||||||
|
|
||||||
// Base colors - a larger palette than the default
|
// Base colors - a larger palette than the default
|
||||||
const colors = [
|
const colors = [
|
||||||
[54, 162, 235], // blue
|
[54, 162, 235], // blue
|
||||||
|
42
proprietary/src/main/resources/templates/AUDIT_HELP.md
Normal file
42
proprietary/src/main/resources/templates/AUDIT_HELP.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Audit System Help
|
||||||
|
|
||||||
|
## About the Audit System
|
||||||
|
The Stirling PDF audit system records user actions and system events for security monitoring, compliance, and troubleshooting purposes.
|
||||||
|
|
||||||
|
## Audit Levels
|
||||||
|
|
||||||
|
| Level | Name | Description | Use Case |
|
||||||
|
|-------|------|-------------|----------|
|
||||||
|
| 0 | OFF | Minimal auditing, only critical security events | Development environments |
|
||||||
|
| 1 | BASIC | Authentication events, security events, and errors | Production environments with minimal storage |
|
||||||
|
| 2 | STANDARD | All HTTP requests and operations (default) | Normal production use |
|
||||||
|
| 3 | VERBOSE | Detailed information including headers, parameters, and results | Troubleshooting and detailed analysis |
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
Audit settings are configured in the `settings.yml` file under the `premium.proFeatures.audit` section:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
premium:
|
||||||
|
proFeatures:
|
||||||
|
audit:
|
||||||
|
enabled: true # Enable/disable audit logging
|
||||||
|
level: 2 # Audit level (0=OFF, 1=BASIC, 2=STANDARD, 3=VERBOSE)
|
||||||
|
retentionDays: 90 # Number of days to retain audit logs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Event Types
|
||||||
|
|
||||||
|
### BASIC Events:
|
||||||
|
- USER_LOGIN - User login
|
||||||
|
- USER_LOGOUT - User logout
|
||||||
|
- USER_FAILED_LOGIN - Failed login attempt
|
||||||
|
- USER_PROFILE_UPDATE - User or profile operations
|
||||||
|
|
||||||
|
### STANDARD Events:
|
||||||
|
- HTTP_REQUEST - GET requests for viewing
|
||||||
|
- PDF_PROCESS - PDF processing operations
|
||||||
|
- FILE_OPERATION - File-related operations
|
||||||
|
- SETTINGS_CHANGED - System or admin settings operations
|
||||||
|
|
||||||
|
### VERBOSE Events:
|
||||||
|
- Detailed versions of STANDARD events with parameters and results
|
File diff suppressed because it is too large
Load Diff
@ -1636,6 +1636,83 @@ validateSignature.cert.keyUsage=Key Usage
|
|||||||
validateSignature.cert.selfSigned=Self-Signed
|
validateSignature.cert.selfSigned=Self-Signed
|
||||||
validateSignature.cert.bits=bits
|
validateSignature.cert.bits=bits
|
||||||
|
|
||||||
|
# Audit Dashboard
|
||||||
|
audit.dashboard.title=Audit Dashboard
|
||||||
|
audit.dashboard.systemStatus=Audit System Status
|
||||||
|
audit.dashboard.status=Status
|
||||||
|
audit.dashboard.enabled=Enabled
|
||||||
|
audit.dashboard.disabled=Disabled
|
||||||
|
audit.dashboard.currentLevel=Current Level
|
||||||
|
audit.dashboard.retentionPeriod=Retention Period
|
||||||
|
audit.dashboard.days=days
|
||||||
|
audit.dashboard.totalEvents=Total Events
|
||||||
|
|
||||||
|
# Audit Dashboard Tabs
|
||||||
|
audit.dashboard.tab.dashboard=Dashboard
|
||||||
|
audit.dashboard.tab.events=Audit Events
|
||||||
|
audit.dashboard.tab.export=Export
|
||||||
|
# Dashboard Charts
|
||||||
|
audit.dashboard.eventsByType=Events by Type
|
||||||
|
audit.dashboard.eventsByUser=Events by User
|
||||||
|
audit.dashboard.eventsOverTime=Events Over Time
|
||||||
|
audit.dashboard.period.7days=7 Days
|
||||||
|
audit.dashboard.period.30days=30 Days
|
||||||
|
audit.dashboard.period.90days=90 Days
|
||||||
|
|
||||||
|
# Events Tab
|
||||||
|
audit.dashboard.auditEvents=Audit Events
|
||||||
|
audit.dashboard.filter.eventType=Event Type
|
||||||
|
audit.dashboard.filter.allEventTypes=All event types
|
||||||
|
audit.dashboard.filter.user=User
|
||||||
|
audit.dashboard.filter.userPlaceholder=Filter by user
|
||||||
|
audit.dashboard.filter.startDate=Start Date
|
||||||
|
audit.dashboard.filter.endDate=End Date
|
||||||
|
audit.dashboard.filter.apply=Apply Filters
|
||||||
|
audit.dashboard.filter.reset=Reset Filters
|
||||||
|
|
||||||
|
# Table Headers
|
||||||
|
audit.dashboard.table.id=ID
|
||||||
|
audit.dashboard.table.time=Time
|
||||||
|
audit.dashboard.table.user=User
|
||||||
|
audit.dashboard.table.type=Type
|
||||||
|
audit.dashboard.table.details=Details
|
||||||
|
|
||||||
|
# Pagination
|
||||||
|
audit.dashboard.pagination.show=Show
|
||||||
|
audit.dashboard.pagination.entries=entries
|
||||||
|
audit.dashboard.pagination.pageInfo1=Page
|
||||||
|
audit.dashboard.pagination.pageInfo2=of
|
||||||
|
audit.dashboard.pagination.totalRecords=Total records:
|
||||||
|
|
||||||
|
# Modal
|
||||||
|
audit.dashboard.modal.eventDetails=Event Details
|
||||||
|
audit.dashboard.modal.id=ID
|
||||||
|
audit.dashboard.modal.user=User
|
||||||
|
audit.dashboard.modal.type=Type
|
||||||
|
audit.dashboard.modal.time=Time
|
||||||
|
audit.dashboard.modal.data=Data
|
||||||
|
|
||||||
|
# Export Tab
|
||||||
|
audit.dashboard.export.title=Export Audit Data
|
||||||
|
audit.dashboard.export.format=Export Format
|
||||||
|
audit.dashboard.export.csv=CSV (Comma Separated Values)
|
||||||
|
audit.dashboard.export.json=JSON (JavaScript Object Notation)
|
||||||
|
audit.dashboard.export.button=Export Data
|
||||||
|
audit.dashboard.export.infoTitle=Export Information
|
||||||
|
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.
|
||||||
|
audit.dashboard.export.infoDesc2=Exported data will include:
|
||||||
|
audit.dashboard.export.infoItem1=Event ID
|
||||||
|
audit.dashboard.export.infoItem2=User
|
||||||
|
audit.dashboard.export.infoItem3=Event Type
|
||||||
|
audit.dashboard.export.infoItem4=Timestamp
|
||||||
|
audit.dashboard.export.infoItem5=Event Data
|
||||||
|
|
||||||
|
# JavaScript i18n keys
|
||||||
|
audit.dashboard.js.noEventsFound=No audit events found matching the current filters
|
||||||
|
audit.dashboard.js.errorLoading=Error loading data:
|
||||||
|
audit.dashboard.js.errorRendering=Error rendering table:
|
||||||
|
audit.dashboard.js.loadingPage=Loading page
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# Cookie banner #
|
# Cookie banner #
|
||||||
####################
|
####################
|
||||||
|
Loading…
x
Reference in New Issue
Block a user