first commit
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
/* mGuard VPN Manager - Custom Styles */
|
||||
|
||||
/* Body */
|
||||
body {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Gateway Status Indicators */
|
||||
.status-online {
|
||||
color: #198754;
|
||||
}
|
||||
|
||||
.status-offline {
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.status-indicator.online {
|
||||
background-color: #198754;
|
||||
box-shadow: 0 0 8px #198754;
|
||||
}
|
||||
|
||||
.status-indicator.offline {
|
||||
background-color: #6c757d;
|
||||
}
|
||||
|
||||
/* Gateway Cards */
|
||||
.gateway-card {
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.gateway-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.gateway-card.online {
|
||||
border-left: 4px solid #198754;
|
||||
}
|
||||
|
||||
.gateway-card.offline {
|
||||
border-left: 4px solid #6c757d;
|
||||
}
|
||||
|
||||
/* Dashboard Stats */
|
||||
.stat-card {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.stat-card .stat-icon {
|
||||
font-size: 2.5rem;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.stat-card .stat-value {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.table-hover tbody tr {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Login Page */
|
||||
.login-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #0d6efd 0%, #0a58ca 100%);
|
||||
}
|
||||
|
||||
.login-box {
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.login-logo {
|
||||
font-size: 3rem;
|
||||
color: #0d6efd;
|
||||
}
|
||||
|
||||
/* HTMX Loading Indicator */
|
||||
.htmx-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.htmx-request .htmx-indicator {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.htmx-request.htmx-indicator {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Sidebar (optional) */
|
||||
.sidebar {
|
||||
min-width: 250px;
|
||||
max-width: 250px;
|
||||
min-height: 100vh;
|
||||
background: #212529;
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
color: rgba(255,255,255,.8);
|
||||
text-decoration: none;
|
||||
padding: 10px 15px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sidebar a:hover {
|
||||
background: rgba(255,255,255,.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.sidebar a.active {
|
||||
background: #0d6efd;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Badges */
|
||||
.badge-role-super_admin {
|
||||
background-color: #dc3545;
|
||||
}
|
||||
|
||||
.badge-role-admin {
|
||||
background-color: #0d6efd;
|
||||
}
|
||||
|
||||
.badge-role-technician {
|
||||
background-color: #198754;
|
||||
}
|
||||
|
||||
.badge-role-viewer {
|
||||
background-color: #6c757d;
|
||||
}
|
||||
|
||||
/* Endpoint Protocol Badges */
|
||||
.badge-tcp {
|
||||
background-color: #0d6efd;
|
||||
}
|
||||
|
||||
.badge-udp {
|
||||
background-color: #6610f2;
|
||||
}
|
||||
|
||||
/* PKI Initialization Banner */
|
||||
.pki-init-banner {
|
||||
background: #ffc107;
|
||||
color: #212529;
|
||||
padding: 15px 20px;
|
||||
text-align: center;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1100;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
|
||||
border-bottom: 3px solid #e0a800;
|
||||
}
|
||||
|
||||
.pki-init-banner .small {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.pki-init-banner .spinner-border {
|
||||
color: #212529 !important;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.stat-card .stat-value {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.pki-init-banner {
|
||||
padding: 10px 15px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// mGuard VPN Manager - Custom JavaScript
|
||||
|
||||
// HTMX Configuration
|
||||
document.body.addEventListener('htmx:configRequest', function(evt) {
|
||||
// Add CSRF token to all HTMX requests
|
||||
// evt.detail.headers['X-CSRFToken'] = document.querySelector('meta[name="csrf-token"]')?.content;
|
||||
});
|
||||
|
||||
// Show loading indicator during HTMX requests
|
||||
document.body.addEventListener('htmx:beforeRequest', function(evt) {
|
||||
// Optional: Show global loading indicator
|
||||
});
|
||||
|
||||
document.body.addEventListener('htmx:afterRequest', function(evt) {
|
||||
// Optional: Hide global loading indicator
|
||||
});
|
||||
|
||||
// Handle HTMX errors
|
||||
document.body.addEventListener('htmx:responseError', function(evt) {
|
||||
console.error('HTMX Error:', evt.detail);
|
||||
// Show error toast or alert
|
||||
});
|
||||
|
||||
// Initialize tooltips
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Bootstrap tooltips
|
||||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||
tooltipTriggerList.map(function(tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl);
|
||||
});
|
||||
|
||||
// Bootstrap popovers
|
||||
var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
|
||||
popoverTriggerList.map(function(popoverTriggerEl) {
|
||||
return new bootstrap.Popover(popoverTriggerEl);
|
||||
});
|
||||
});
|
||||
|
||||
// Confirm delete dialogs
|
||||
function confirmDelete(message, formId) {
|
||||
if (confirm(message || 'Wirklich löschen?')) {
|
||||
document.getElementById(formId).submit();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Auto-refresh gateway status
|
||||
function refreshGatewayStatus() {
|
||||
// Handled by HTMX polling
|
||||
}
|
||||
|
||||
// Format relative time
|
||||
function formatRelativeTime(dateString) {
|
||||
if (!dateString) return 'Nie';
|
||||
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
const diff = Math.floor((now - date) / 1000);
|
||||
|
||||
if (diff < 60) return 'Gerade eben';
|
||||
if (diff < 3600) return Math.floor(diff / 60) + ' Min.';
|
||||
if (diff < 86400) return Math.floor(diff / 3600) + ' Std.';
|
||||
return Math.floor(diff / 86400) + ' Tage';
|
||||
}
|
||||
|
||||
// Update all relative times on page
|
||||
function updateRelativeTimes() {
|
||||
document.querySelectorAll('[data-relative-time]').forEach(function(el) {
|
||||
el.textContent = formatRelativeTime(el.dataset.relativeTime);
|
||||
});
|
||||
}
|
||||
|
||||
// Update times every minute
|
||||
setInterval(updateRelativeTimes, 60000);
|
||||
|
||||
// Copy to clipboard
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text).then(function() {
|
||||
// Show success toast
|
||||
console.log('Copied to clipboard');
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user