Files
winauthmon-server/templates/frontend/dashboard.html
2025-05-25 20:26:18 +01:00

460 lines
19 KiB
HTML

{% extends "base.html" %}
{% block head %}
<!-- DataTables CSS -->
<link href="{{ url_for('static', filename='css/dataTables.bootstrap5.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/buttons.bootstrap5.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/buttons.dataTables.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/colVis.dataTables.min.css') }}" rel="stylesheet">
<!-- DateRangePicker CSS -->
<link href="{{ url_for('static', filename='css/daterangepicker.css') }}" rel="stylesheet">
<!-- Custom DateRangePicker dark theme styles -->
<style>
/* Dark theme for date picker */
.daterangepicker {
background-color: #212529;
border-color: #495057;
color: #f8f9fa;
}
.daterangepicker .calendar-table {
background-color: #343a40;
border-color: #495057;
}
.daterangepicker td.available:hover,
.daterangepicker th.available:hover {
background-color: #495057;
}
.daterangepicker td.active,
.daterangepicker td.active:hover {
background-color: #0d6efd;
color: #fff;
}
/* In-between dates in the selected range - lighter blue */
.daterangepicker td.in-range {
background-color: #82b1ff; /* Lighter blue */
color: #212529; /* Darker text for better contrast */
}
.daterangepicker td.in-range:hover {
background-color: #75a7f7; /* Slightly darker when hovering */
color: #212529;
}
.daterangepicker .calendar-table .next span,
.daterangepicker .calendar-table .prev span {
border-color: #f8f9fa;
}
.daterangepicker .ranges li:hover,
.daterangepicker .ranges li.active {
background-color: #0d6efd;
color: #fff;
}
.daterangepicker .ranges li {
color: #f8f9fa;
}
.daterangepicker:after {
border-bottom-color: #212529;
}
.daterangepicker:before {
border-bottom-color: #495057;
}
/* Calendar header and weekday styling */
.daterangepicker .calendar-table th {
color: #f8f9fa;
}
/* Month name */
.daterangepicker .month {
color: #f8f9fa;
}
/* Off days (not in current month) */
.daterangepicker td.off {
color: #6c757d;
}
/* Input boxes */
.daterangepicker input.input-mini {
background-color: #343a40;
border-color: #495057;
color: #f8f9fa;
}
/* Time picker */
.daterangepicker .calendar-time select {
background-color: #343a40;
border-color: #495057;
color: #f8f9fa;
}
/* Apply and cancel buttons */
.daterangepicker .drp-buttons {
border-top-color: #495057;
}
.daterangepicker .drp-buttons .btn {
color: #f8f9fa;
}
/* Input fields focus */
.daterangepicker input.input-mini:focus {
border-color: #0d6efd;
}
/* Time inputs container */
.daterangepicker .calendar-time {
background-color: #343a40;
border-color: #495057;
}
/* Make the export buttons more visible */
.dt-buttons {
margin-top: 10px;
margin-bottom: 15px;
display: inline-block !important;
}
.dt-button {
margin-right: 5px;
}
/* Page title and export buttons container */
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 15px;
}
.page-title {
margin-bottom: 0;
}
/* For smaller screens */
@media (max-width: 768px) {
.page-header {
flex-direction: column;
align-items: flex-start;
}
.export-buttons {
margin-top: 10px;
}
}
/* Column visibility dropdown styles */
.dropdown-menu {
background-color: #343a40;
border-color: #495057;
}
.dropdown-item {
color: #f8f9fa;
}
.dropdown-item:hover, .dropdown-item:focus {
background-color: #495057;
color: #f8f9fa;
}
.form-check-input:checked {
background-color: #0d6efd;
border-color: #0d6efd;
}
.form-check-label {
color: #f8f9fa;
}
#column-visibility-menu {
min-width: 200px;
}
.column-checkbox {
padding: 0.375rem 1rem;
}
</style>
{% endblock %}
{% block title %}Dashboard{% endblock %}
{% block content %}
<div class="container-fluid mt-4">
<div class="page-header">
<h2 class="page-title">Login Events Dashboard</h2>
<div class="export-buttons">
<div class="btn-group me-2" role="group">
<button id="column-visibility" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
Columns
</button>
<ul class="dropdown-menu" id="column-visibility-menu">
<!-- Column visibility checkboxes will be populated by JavaScript -->
</ul>
</div>
<button id="export-csv" class="btn btn-secondary btn-sm">Export CSV</button>
<button id="export-excel" class="btn btn-success btn-sm">Export Excel</button>
<button id="print-table" class="btn btn-info btn-sm">Print</button>
</div>
</div>
<div class="card mt-3 mb-3">
<div class="card-body">
<form id="dateRangeForm" method="GET" action="{{ url_for('frontend.dashboard') }}">
<div class="row align-items-end">
<div class="col-md-3">
<label for="daterange" class="form-label">Date Range:</label>
<input type="text" id="daterange" name="daterange" class="form-control"
value="{{ start_date.strftime('%Y-%m-%d %H:%M') if start_date else '' }} - {{ end_date.strftime('%Y-%m-%d %H:%M') if end_date else '' }}"/>
</div>
{% if companies %}
<div class="col-md-3">
<label for="company_id" class="form-label">Company:</label>
<select class="form-select" id="company_id" name="company_id">
<option value="">All Companies</option>
{% for company in companies %}
<option value="{{ company.id }}" {% if selected_company_id == company.id %}selected{% endif %}>
{{ company.name }}
</option>
{% endfor %}
</select>
</div>
{% endif %}
<div class="col-md-2">
<button type="submit" class="btn btn-primary">Apply Filter</button>
</div>
</div>
</form>
</div>
</div>
<div class="card">
<div class="card-body">
<table id="logsTable" class="table table-striped table-bordered" style="width:100%">
<thead>
<tr>
<th>Event Type</th>
<th>User Name</th>
<th>Computer Name</th>
<th>IP Address</th>
<th>Site</th>
<th>Timestamp</th>
{% if current_user.is_global_admin() and not selected_company_id %}
<th>Company</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for log in logs %}
<tr>
<td>{{ log.event_type }}</td>
<td>{{ log.user_name }}</td>
<td>{{ log.computer_name }}</td>
<td>{{ log.ip_address }}</td>
<td>{{ log.api_key.description if log.api_key else 'N/A' }}</td>
<td data-order="{{ log.timestamp.strftime('%Y%m%d%H%M%S') }}">
{{ log.timestamp|format_datetime }}
</td>
{% if current_user.is_global_admin() and not selected_company_id %}
<td>{{ log.company.name if log.company else 'N/A' }}</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<!-- DataTables JS -->
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/dataTables.bootstrap5.min.js') }}"></script>
<!-- DataTables Buttons JS -->
<script src="{{ url_for('static', filename='js/dataTables.buttons.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/buttons.bootstrap5.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/buttons.html5.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/buttons.print.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/jszip.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/pdfmake.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/vfs_fonts.js') }}"></script>
<!-- Moment.js -->
<script src="{{ url_for('static', filename='js/moment.min.js') }}"></script>
<!-- DateRangePicker -->
<script src="{{ url_for('static', filename='js/daterangepicker.min.js') }}"></script>
<script>
$(document).ready(function() {
$('#daterange').daterangepicker({
timePicker: true,
timePicker24Hour: true,
timePickerSeconds: false,
timePickerIncrement: 1, // Changed from 15 to 1 minute increments
autoUpdateInput: false, // Prevents auto-update so user can edit manually
locale: {
format: 'YYYY-MM-DD HH:mm',
cancelLabel: 'Clear',
applyLabel: 'Apply'
},
ranges: {
'Last 48 Hours': [moment().subtract(48, 'hours'), moment()],
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
'This Month': [moment().startOf('month'), moment().endOf('month')],
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
}
});
// Handle manual input updates
$('#daterange').on('apply.daterangepicker', function(ev, picker) {
$(this).val(picker.startDate.format('YYYY-MM-DD HH:mm') + ' - ' + picker.endDate.format('YYYY-MM-DD HH:mm'));
});
$('#daterange').on('cancel.daterangepicker', function(ev, picker) {
$(this).val('');
});
// Allow direct editing of the date input
$('#daterange').on('keyup', function(e) {
if(e.keyCode === 13) {
// Try to parse the input value
var parts = $(this).val().split(' - ');
if(parts.length === 2) {
var startDate = moment(parts[0], 'YYYY-MM-DD HH:mm');
var endDate = moment(parts[1], 'YYYY-MM-DD HH:mm');
if(startDate.isValid() && endDate.isValid()) {
var picker = $(this).data('daterangepicker');
picker.setStartDate(startDate);
picker.setEndDate(endDate);
}
}
}
});
var table = $('#logsTable').DataTable({
pageLength: 50,
lengthMenu: [[50, 100, 200, 500, 1000], [50, 100, 200, 500, 1000]],
order: [[{% if current_user.is_global_admin() and not selected_company_id %}6{% else %}5{% endif %}, 'desc']], // Sort by timestamp column descending
dom: '<"row"<"col-sm-12 col-md-6"l><"col-sm-12 col-md-6"f>>' +
'<"row"<"col-sm-12"tr>>' +
'<"row"<"col-sm-12 col-md-5"i><"col-sm-12 col-md-7"p>>',
columnDefs: [
{
targets: [2, 3], // Computer Name and IP Address columns
visible: false // Hide by default
}
],
buttons: [
{
extend: 'csv',
text: 'Export CSV',
className: 'btn btn-secondary',
filename: 'login_events_' + moment().format('YYYY-MM-DD'),
exportOptions: {
columns: ':visible'
}
},
{
extend: 'excel',
text: 'Export Excel',
className: 'btn btn-success',
filename: 'login_events_' + moment().format('YYYY-MM-DD'),
exportOptions: {
columns: ':visible'
}
},
{
extend: 'print',
text: 'Print',
className: 'btn btn-info',
exportOptions: {
columns: ':visible'
}
}
],
language: {
search: "Search records:",
lengthMenu: "Show _MENU_ records per page",
info: "Showing _START_ to _END_ of _TOTAL_ records",
paginate: {
first: "First",
last: "Last",
next: "Next",
previous: "Previous"
}
}
});
// Column names for the visibility controls
var columnNames = [
'Event Type',
'User Name',
'Computer Name',
'IP Address',
'Site',
'Timestamp'
{% if current_user.is_global_admin() and not selected_company_id %},
'Company'
{% endif %}
];
// Load saved column visibility from localStorage
function loadColumnVisibility() {
var saved = localStorage.getItem('dashboardColumnVisibility');
if (saved) {
try {
return JSON.parse(saved);
} catch (e) {
console.log('Error parsing saved column visibility:', e);
}
}
// Default visibility - hide Computer Name (2) and IP Address (3)
var defaultVisibility = {};
columnNames.forEach(function(name, index) {
defaultVisibility[index] = index !== 2 && index !== 3;
});
return defaultVisibility;
}
// Save column visibility to localStorage
function saveColumnVisibility(visibility) {
localStorage.setItem('dashboardColumnVisibility', JSON.stringify(visibility));
}
// Apply saved column visibility to table
var savedVisibility = loadColumnVisibility();
Object.keys(savedVisibility).forEach(function(colIndex) {
table.column(parseInt(colIndex)).visible(savedVisibility[colIndex]);
});
// Create column visibility dropdown menu
function createColumnVisibilityMenu() {
var menu = $('#column-visibility-menu');
menu.empty();
columnNames.forEach(function(columnName, index) {
var isVisible = table.column(index).visible();
var checkboxId = 'col-vis-' + index;
var menuItem = $('<li class="column-checkbox"></li>');
var formCheck = $('<div class="form-check"></div>');
var checkbox = $('<input class="form-check-input" type="checkbox" id="' + checkboxId + '"' +
(isVisible ? ' checked' : '') + '>');
var label = $('<label class="form-check-label" for="' + checkboxId + '">' + columnName + '</label>');
checkbox.on('change', function() {
var colIndex = parseInt(this.id.split('-')[2]);
var isChecked = this.checked;
table.column(colIndex).visible(isChecked);
// Update saved visibility
savedVisibility[colIndex] = isChecked;
saveColumnVisibility(savedVisibility);
});
formCheck.append(checkbox, label);
menuItem.append(formCheck);
menu.append(menuItem);
});
}
// Initialize column visibility menu
createColumnVisibilityMenu();
// Prevent dropdown from closing when clicking inside
$('#column-visibility-menu').on('click', function(e) {
e.stopPropagation();
});
// Connect the custom export buttons to DataTables buttons
$('#export-csv').on('click', function() {
table.button('.buttons-csv').trigger();
});
$('#export-excel').on('click', function() {
table.button('.buttons-excel').trigger();
});
$('#print-table').on('click', function() {
table.button('.buttons-print').trigger();
});
});
</script>
{% endblock %}