diff --git a/proprietary/src/main/resources/static/css/audit-dashboard.css b/proprietary/src/main/resources/static/css/audit-dashboard.css index 15bac161c..87bc3a27c 100644 --- a/proprietary/src/main/resources/static/css/audit-dashboard.css +++ b/proprietary/src/main/resources/static/css/audit-dashboard.css @@ -2,6 +2,19 @@ margin-bottom: 20px; border-radius: 8px; 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 { text-align: center; @@ -13,7 +26,7 @@ } .stat-label { font-size: 1rem; - color: #666; + color: var(--md-sys-color-on-surface-variant); } .chart-container { position: relative; @@ -23,6 +36,9 @@ .filter-card { margin-bottom: 20px; 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 { position: absolute; @@ -30,7 +46,7 @@ left: 0; width: 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; justify-content: center; align-items: center; @@ -44,26 +60,42 @@ font-weight: bold; } .level-0 { - background-color: #dc3545; /* Red */ + background-color: var(--md-sys-color-error, #dc3545); /* Red */ } .level-1 { - background-color: #fd7e14; /* Orange */ + background-color: var(--md-sys-color-secondary, #fd7e14); /* Orange */ } .level-2 { - background-color: #28a745; /* Green */ + background-color: var(--md-nav-section-color-other, #28a745); /* Green */ } .level-3 { - background-color: #17a2b8; /* Teal */ + background-color: var(--md-sys-color-tertiary, #17a2b8); /* Teal */ } /* Custom data table styling */ .audit-table { 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 { - background-color: #f8f9fa; + background-color: var(--md-sys-color-surface-container-high); + color: var(--md-sys-color-on-surface); position: sticky; top: 0; z-index: 10; + font-weight: bold; } .table-responsive { max-height: 600px; @@ -74,7 +106,8 @@ align-items: center; margin-top: 15px; 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 { @@ -93,13 +126,15 @@ background-color: var(--bs-light); } .json-viewer { - background-color: #f8f9fa; + background-color: var(--md-sys-color-surface-container-low); + color: var(--md-sys-color-on-surface); border-radius: 4px; padding: 10px; max-height: 300px; overflow-y: auto; font-family: monospace; white-space: pre-wrap; + border: 1px solid var(--md-sys-color-outline-variant); } /* Simple, minimal radio styling - no extras */ @@ -113,14 +148,14 @@ right: 0; width: 400px; height: 200px; - background: rgba(0,0,0,0.8); - color: #0f0; + background: var(--md-sys-color-surface-container-highest, rgba(0,0,0,0.8)); + color: var(--md-sys-color-tertiary, #0f0); font-family: monospace; font-size: 12px; z-index: 9999; overflow-y: auto; 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 */ } @@ -128,13 +163,64 @@ label.btn-outline-primary { cursor: pointer; transition: all 0.2s; + border-color: var(--md-sys-color-primary); + color: var(--md-sys-color-primary); } label.btn-outline-primary.active { - background-color: var(--bs-primary); - color: white; + background-color: var(--md-sys-color-primary); + color: var(--md-sys-color-on-primary); + border-color: var(--md-sys-color-primary); } label.btn-outline-primary input[type="radio"] { 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); } \ No newline at end of file diff --git a/proprietary/src/main/resources/static/js/audit/dashboard.js b/proprietary/src/main/resources/static/js/audit/dashboard.js index f5cf6324c..a3f10f3c0 100644 --- a/proprietary/src/main/resources/static/js/audit/dashboard.js +++ b/proprietary/src/main/resources/static/js/audit/dashboard.js @@ -80,6 +80,31 @@ document.addEventListener('keydown', function(e) { }); // 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() { debugLog('Page initialized'); @@ -106,7 +131,7 @@ document.addEventListener('DOMContentLoaded', function() { // Show a loading message immediately if (auditTableBody) { auditTableBody.innerHTML = - '
Loading audit data...'; + '
' + window.i18n.loading + ''; } else { debugLog('ERROR: auditTableBody element not found!'); } @@ -117,6 +142,9 @@ document.addEventListener('DOMContentLoaded', function() { // Load statistics for dashboard loadStats(7); + // Setup theme change listener + setupThemeChangeListener(); + // Set up event listeners pageSizeSelect.addEventListener('change', function() { pageSize = parseInt(this.value); @@ -350,7 +378,7 @@ function loadAuditData(targetPage, realPageSize) { .catch(error => { debugLog('Error loading data', error.message); if (auditTableBody) { - auditTableBody.innerHTML = `Error loading data: ${error.message}`; + auditTableBody.innerHTML = `${window.i18n.errorLoading} ${error.message}`; } hideLoading('table-loading'); @@ -375,6 +403,8 @@ function loadStats(days) { .then(response => response.json()) .then(data => { document.getElementById('total-events').textContent = data.totalEvents; + // Cache stats data for theme changes + window.cachedStatsData = data; renderCharts(data); hideLoading('type-chart-loading'); hideLoading('user-chart-loading'); @@ -412,7 +442,7 @@ function renderTable(events) { if (!events || events.length === 0) { debugLog('No events to render'); - auditTableBody.innerHTML = 'No audit events found matching the current filters'; + auditTableBody.innerHTML = '' + window.i18n.noEventsFound + ''; return; } @@ -452,7 +482,7 @@ function renderTable(events) { debugLog('Table rendering complete'); } catch (e) { debugLog('Error in renderTable', e.message); - auditTableBody.innerHTML = 'Error rendering table: ' + e.message + ''; + auditTableBody.innerHTML = '' + window.i18n.errorRendering + ' ' + e.message + ''; } } @@ -521,6 +551,9 @@ function goToPage(page) { // Render charts function renderCharts(data) { + // Get theme colors + const colors = getThemeColors(); + // Prepare data for charts const typeLabels = Object.keys(data.eventsByType); const typeValues = Object.values(data.eventsByType); @@ -532,6 +565,10 @@ function renderCharts(data) { const timeLabels = Object.keys(data.eventsByDay).sort(); 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 if (typeChart) { typeChart.destroy(); @@ -543,19 +580,84 @@ function renderCharts(data) { data: { labels: typeLabels, datasets: [{ - label: 'Events by Type', + label: window.i18n.eventsByType, data: typeValues, - backgroundColor: getChartColors(typeLabels.length), - borderColor: getChartColors(typeLabels.length, 1), // Full opacity for borders + backgroundColor: colors.chartColors.slice(0, typeLabels.length), + borderColor: colors.chartColors.slice(0, typeLabels.length), borderWidth: 1 }] }, options: { responsive: true, 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: { 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: { labels: userLabels, datasets: [{ - label: 'Events by User', + label: window.i18n.eventsByUser, data: userValues, - backgroundColor: getChartColors(userLabels.length), - borderWidth: 1 + backgroundColor: colors.chartColors.slice(0, userLabels.length), + borderWidth: 1, + borderColor: colors.isDarkMode ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.2)' }] }, options: { 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: { labels: timeLabels, datasets: [{ - label: 'Events Over Time', + label: window.i18n.eventsOverTime, data: timeValues, - backgroundColor: 'rgba(75, 192, 192, 0.2)', - borderColor: 'rgba(75, 192, 192, 1)', + backgroundColor: colors.chartColors[0] + '40', // 40 = 25% opacity + borderColor: colors.chartColors[0], tension: 0.1, fill: true }] @@ -606,9 +751,74 @@ function renderCharts(data) { options: { responsive: true, 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: { 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 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 const colors = [ [54, 162, 235], // blue diff --git a/proprietary/src/main/resources/templates/AUDIT_HELP.md b/proprietary/src/main/resources/templates/AUDIT_HELP.md new file mode 100644 index 000000000..be931076c --- /dev/null +++ b/proprietary/src/main/resources/templates/AUDIT_HELP.md @@ -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 \ No newline at end of file diff --git a/proprietary/src/main/resources/templates/audit/dashboard.html b/proprietary/src/main/resources/templates/audit/dashboard.html index 5eea355c6..71c29a1f7 100644 --- a/proprietary/src/main/resources/templates/audit/dashboard.html +++ b/proprietary/src/main/resources/templates/audit/dashboard.html @@ -12,30 +12,30 @@
- +
-

Audit Dashboard

+

Audit Dashboard

-

Audit System Status

+

Audit System Status

-
Status
+
Status
- Enabled - Disabled + Enabled + Disabled
-
Current Level
+
Current Level
STANDARD
@@ -43,13 +43,13 @@
-
Retention Period
-
90 days
+
Retention Period
+
90 days
-
Total Events
+
Total Events
-
@@ -60,16 +60,15 @@ @@ -80,18 +79,18 @@
-

Events by Type

+

Events by Type

- - - + + +
- Loading... + Loading...
@@ -102,13 +101,13 @@
-

Events by User

+

Events by User

- Loading... + Loading...
@@ -121,13 +120,13 @@
-

Events Over Time

+

Events Over Time

- Loading... + Loading...
@@ -142,7 +141,7 @@
-

Audit Events

+

Audit Events

@@ -150,36 +149,36 @@
- +
- - + +
- +
- +
- - + +
@@ -188,17 +187,17 @@
- Loading... + Loading...
- - - - - + + + + + @@ -210,15 +209,15 @@
- Show + Show - entries - Page 1 of 1 (Total records: 0) + entries + Page 1 of 1 (Total records: 0)
IDTimeUserTypeDetailsIDTimeUserTypeDetails
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LevelNameDescriptionUse Case
0OFFMinimal auditing, only critical security eventsDevelopment environments
1BASICAuthentication events, security events, and errorsProduction environments with minimal storage
2STANDARDAll HTTP requests and operations (default)Normal production use
3VERBOSEDetailed information including headers, parameters, and resultsTroubleshooting and detailed analysis
-
- -

Configuration

-

Audit settings are configured in the settings.yml file under the premium.proFeatures.audit section:

-
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
    • -
    -
  • -
    -
-
-
-
@@ -453,830 +361,25 @@
+ + + - - -
- +
diff --git a/stirling-pdf/src/main/resources/messages_en_GB.properties b/stirling-pdf/src/main/resources/messages_en_GB.properties index c337551dd..940431990 100644 --- a/stirling-pdf/src/main/resources/messages_en_GB.properties +++ b/stirling-pdf/src/main/resources/messages_en_GB.properties @@ -1636,6 +1636,83 @@ validateSignature.cert.keyUsage=Key Usage validateSignature.cert.selfSigned=Self-Signed 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 # ####################