Visuelle Verbesserungen
Dieser Commit ist enthalten in:
1
backups/backup_v2docker_20250608_211018_encrypted.sql.gz.enc
Normale Datei
1
backups/backup_v2docker_20250608_211018_encrypted.sql.gz.enc
Normale Datei
@@ -0,0 +1 @@
|
||||
gAAAAABoReAamhCZ8jE9qi5Klhy4BxnffoaWCZn7X-sxrxzHpwCO9-BmDaV9sAr_IAZ78yXiZ5w_OqonOXR68CLJ75DIpi3EiSfbZdxIynni15vkmcoaVZq8MTMjw3_F_O6DSBKw_v6arV_NwnIQEOO0yBYcvIoX9W9QPzWhVnzC1-1vvHIpAyHpWinp1BrJ0jd0ec0dBk3CT5YvnPYZyf1ul8f0zAHbjcWnke1abOWk3GyUJoLLnDPRf6LCLTQf0SKyfnICPiBK_pNM67rOBcs50N645EB0iS9XtZ3_68OgXIYIqvwLVGNAfKXTOrTDFGkIHISCeHH-bd64966lo9KNnGLA_eEzpYTJqB1rO8_5G5trR3dXOVBW09TF73BfoWG9slIn-wNymkHH94XLvyPKGicNYXPnattDDZQtZY-pRdWV4qznYIgEm7Q3qGKZxj-DiyFCGQq7altk9Cm6zQ9ohlneOe4cWlevLFsWGl9Q8za-G7gHj1lTnyvb5O9MhmVAiDF5zvzdAvclF5q_wvc2poPjuZVY1tilT11iUSbtf9vb-0MSoL35wAKmvJU1KXX4xNBmlmC20rvL8BCiaRUsZrmai5Nm8GESMN2x6ohXLevVcU75Bnh4H_2EndgkBNxnj9fpiurlXpSq6NS1A-BQGLLuFgJxjokbkRRO-12R0tvrchA1tr8a9uIF5JyujyKyBABEI-q5SC60Ujcqzst_B7oTzxUTDgbuRk_0IVfz6OzfmqWrhEj40pXyYSyrmcih_TPN99ZOo725qT3zDSAAnNDqe5r_26Nr2-SXB9oZMLztCNBzcU4mXst5ZPeZ--BZh_E7azw18SDMuR-0zleXK30d1MC64-2T_dePBEagO1Grp2jOfRAraG7V3lqyYZS3w1UD_NoFAYksuJzu-msMVc910hDjH1tP01VbHsPULni0Em8J9sJatFQ0JSz9kjHn9DoEqGCaXhmxldcBRK6InL4SS08HjtwvSbbfz-dUBxtrC2p6HevYb1kDJAS2JrwS_HunjMpL05ZHKm-CeNqsoNKjAGBeqr3kERrFrmja7qBl_aPPJmeSh7_nvVDh6tc_F9UiDkMj3ScGAEMeJkoewBk51oNEspmdePydudsv1kht9lL8GSK3FoX3d-WQ2K92XY6Tw-VXm1JvCwDVpAy6kBUiCWS0KG9qE0O6BUU5J0JMc4GjYDBVe7sP-O1AcOe__rZ4oWqMHwmv7q3uNFkY98KZGPqVdg2kUNgtdBjzRjJDxbfUqvGP5c6PbWRFO0QKGYtbU0C7H6d-Bwi3suo65-9A6FBp6yZX6PwXBXLU2hz_d06szYdON4qZMwu9UwcvISm4jCuK_Qh6v8UyL_VfeZrt7VD4S7ChOWxs405h6-pxhH744N-wwJ-BpjyqOUfIn9Vv2-axY4JshNBOuk2dmZzNxXPW-u5O4rnBGZUnYdsuULa3YCR6JtC76t59EnbLpQO5S3EFw2_f6wfpiJc3RGYUu9rZQq_xPPwWgewWKxsvnhX3eRQGAdK50TwL86T0vP-e45Z-tGqI05WocIbxUr9SE2Xx1mqxe2prLK6rakulwNbwKkizsaK4GVTeya0PV8pLqVdIirqSbWcsgsP969b5Vz27clFpS_QZCG5eAYxUkNXKbwSEWcif-rXtudNBlrI9cS6zgC-d6RuDKbqdwe83Sb67W-_xjEE34DDod_jQe5Es4jnwDcBjUpgv8UKv1aOia3ICNIE5TgiufBmp_qA3lw0iyH-gk2oXr-p20sG-DIbcCALx9eP3pup25ow36nzUEqIGCG7YSN6kT5MUThKdiW0wu3AeWN9QCBaGUxoEU9OGGwOQx5kDkBVhBwvc2G0IrlZYzNHnkoawPRvHjpDbqFruAJIzU9hwVR-54mKqe7pJL3YuDzud7GK5c4xAgm4zF8v4lUMF1MkjmXvJFAI3wDLgx5i_J8U7VkDRjoClij_EZ27YTvPfheFA-_F1WLE3Cpc353uOyCt3vvPHHcH1UeazshgOIaVzdjuH3gzi4iUDP0MT7rWQJo-v0Ace3N5iYsZAkhtWVLGIa4tke174j97riWsqAQTnIz5unlTCSOieLtGprNxdm9FVPPVLdg5GWsz-wJ6aMq3Qm26yS01VBlzKud0rVU28hNaYj9Inyn_zwUm9sMC2XCTLFKbMqWw4CgH0udCSsMZUze7kEPjQF1wsJ-DQ54DbXZ_Ccb3og79qvaIm19XVEWA5gJ1qARpmWNdsrSJhEeMoJD5tkLGUwuPHRh7ADh8095EIrDCpbV0kRNlQzh5Q-_UX2XqbDX8aAPxuQGM0gdfmqSG_vaoYxYqJuA9CSasgelqlS1tYXpDxwlQALEmleebolT3HR-6hKNhTRDiuoCaxEuM5raPXq0HkB_NAnkNIxoVlVCFP1FQxs65YDtoym9dhfc5_Hqe6xVdDBDrMp-H2E2kCfBEMrs_ZR75uVO2j26SZqVLLy1iR_ER-u7uFZnWlH-3_fqkoJcYAbIQGZMe8wtOUfcvY5pPEzA9NNsuOqU3i4JO--Lin1Jiy9eowGx8ZtpxAsALb_X8rSOuZBIO8rf3zFEp8WDWKwdhTcoOJxL_tXBJ-BRCX8XRxxPKoas-xv6jq48O4gkQwh2ydfdQ-aN7su4k9maMx98uFBErqbrYIi9gs4dw60uxNl3whjxJVrO8pNjhRaHe0-_-l_LRb4MnCJL4OfmPxdHE7ecqCb5npbOIdI15AD93DB_cEQYzD3ZoBj_2LNT3NGUHLdy2WzLnd7Yudohi8LBymo1DBmWKnK1-lrhn58EhK1jMDrtuzETMmfNGR5sRY4OhCM7W32WxQqNO0gbQE4d09YLx8RGX4hi0hKP4kVeZFQLu8EauLkKTw_fxCtYs2p0cHu5u4mlUd-YI0_UTK01l6jomOQq9Bt12q0YPztKgdJZYDMbAsmdZrGfZHOSnCG44OD97Gq3QwNp6n7GwbW5qq36ybBbedc3BYK6101yqwVGZkDNBs4wrNPy9ZkJDxMEEaLBVshcJeOsw7uk7giozbc8s4PCvJwYaJNNoJe7bj-E-CSmdiXsBnFPY7DEfcjctDz05gcOmCAb1uMqXObvKooP8P0nUZojfVFkA6y6OZzCWza2-gtr2_DsQd3pn7aYxhf7JsGY3V652cYFlcuASbpH4BBbk5SRnPsCfrYDnLDmurFkMn0tPg8Bj2kyJ4wgh9vcY9AvxFpYiDI6gnAVa_eYijAmfOTczTk3SV3Yl0srRFhYYkutXLatf2pm3_aMg3HbB5N91kNqf4hJgcroJugvts1SeiGFKu6nQFhJdaoBBcEQQRsgEvyZiRF-HT5TMfg-_xY0-bmoPepd1qtvrR4q_sfubFEfwc9ExWPZPAkq6QPscYFXxaIX25mUKD8QkiEivhY2vEv3C8y9vyvG01KEeKyQXRBWLMMKWenPA6bpauVfgqPIXe3qvmMRwBKIugYAxZPKugMMOXSNcd-eYnQAgJ9kTDDajEeGyRyPNdgDYaIMvoEpZvMsOJ7_IcQeeMB88l9KUCC9F0FRjhwrFVFyHcoGdlUv4dg8bRvFQJWpwwqWhM1y3DPgaWOn28BO4bPY_X8CnYIqmMK7buWarpFlQMQC5bbuaeO_JXzuSn_w5Ap2wEcJFHvqqUy0YUsYBswomvvQbmqaIVpH74sbhfQu8OGS0vdtnA-3JggvNxv3YHrv6NExazJs2IMRbqr1hXwQj0G0oPj690j39trbZLNuCZpejjTrMu2npyy_mIFRAYEKT7eibBqcFrDcxAjf2G7t59eVi06ecVOq2O8o4cclFQrMwjMm4a66P3CFmsZldqecFSR0nIC_yW14clyvwu3rvCPssWwAkVzl_uF7RbVMHi6RgyWwuZ8sEGyoZ1p0TFlFFF0bMuSuMGVb8NG_sQl5faneRPjCh2f39ApoxciHD-bRgwhsS54jtoxSi0AFKzrjFK4PsWuCY6Qt3VU80hPv_3RlbVjALLise8w_0J_jQmQ7Z7Xrc6feNrYrg=
|
||||
@@ -9,6 +9,28 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" rel="stylesheet" />
|
||||
{% block extra_css %}{% endblock %}
|
||||
<style>
|
||||
/* Global Status Colors */
|
||||
:root {
|
||||
--status-active: #28a745;
|
||||
--status-warning: #ffc107;
|
||||
--status-danger: #dc3545;
|
||||
--status-inactive: #6c757d;
|
||||
--status-info: #17a2b8;
|
||||
}
|
||||
|
||||
/* Status Classes - Global */
|
||||
.status-aktiv { color: var(--status-active) !important; }
|
||||
.status-ablaufend { color: var(--status-warning) !important; }
|
||||
.status-abgelaufen { color: var(--status-danger) !important; }
|
||||
.status-deaktiviert { color: var(--status-inactive) !important; }
|
||||
|
||||
/* Badge Variants */
|
||||
.badge-aktiv { background-color: var(--status-active) !important; }
|
||||
.badge-ablaufend { background-color: var(--status-warning) !important; color: #000 !important; }
|
||||
.badge-abgelaufen { background-color: var(--status-danger) !important; }
|
||||
.badge-deaktiviert { background-color: var(--status-inactive) !important; }
|
||||
|
||||
/* Session Timer Styles */
|
||||
#session-timer {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
@@ -19,23 +41,23 @@
|
||||
}
|
||||
|
||||
.timer-normal {
|
||||
background-color: #28a745;
|
||||
background-color: var(--status-active);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.timer-warning {
|
||||
background-color: #ffc107;
|
||||
background-color: var(--status-warning);
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.timer-danger {
|
||||
background-color: #dc3545;
|
||||
background-color: var(--status-danger);
|
||||
color: white;
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
|
||||
.timer-critical {
|
||||
background-color: #dc3545;
|
||||
background-color: var(--status-danger);
|
||||
color: white;
|
||||
animation: blink 0.5s infinite;
|
||||
}
|
||||
|
||||
@@ -5,24 +5,68 @@
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.stat-card {
|
||||
transition: transform 0.2s;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
.stat-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
||||
box-shadow: 0 6px 12px rgba(0,0,0,0.15);
|
||||
}
|
||||
.stat-card .card-icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.stat-card .card-value {
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
.stat-card .card-label {
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
a:hover .stat-card {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
||||
box-shadow: 0 6px 12px rgba(0,0,0,0.15);
|
||||
}
|
||||
.text-decoration-none:hover {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.status-aktiv { color: #28a745; }
|
||||
.status-ablaufend { color: #ffc107; }
|
||||
.status-abgelaufen { color: #dc3545; }
|
||||
.status-deaktiviert { color: #6c757d; }
|
||||
|
||||
|
||||
/* Session pulse effect */
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.05); opacity: 0.8; }
|
||||
100% { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
.pulse-effect {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
/* Chart styles */
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 100px;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
/* Progress bar styles */
|
||||
.progress-custom {
|
||||
height: 8px;
|
||||
background-color: #e9ecef;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.progress-bar-custom {
|
||||
background-color: #28a745;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
@@ -43,9 +87,9 @@
|
||||
<a href="/customers" class="text-decoration-none">
|
||||
<div class="card stat-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">👥 Kunden</h5>
|
||||
<h2 class="text-primary">{{ stats.total_customers }}</h2>
|
||||
<p class="text-muted mb-0">Gesamt</p>
|
||||
<div class="card-icon text-primary">👥</div>
|
||||
<div class="card-value text-primary">{{ stats.total_customers }}</div>
|
||||
<div class="card-label text-muted">Kunden Gesamt</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@@ -54,9 +98,12 @@
|
||||
<a href="/licenses" class="text-decoration-none">
|
||||
<div class="card stat-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">📋 Lizenzen</h5>
|
||||
<h2 class="text-info">{{ stats.total_licenses }}</h2>
|
||||
<p class="text-muted mb-0">Gesamt</p>
|
||||
<div class="card-icon text-info">📋</div>
|
||||
<div class="card-value text-info">{{ stats.total_licenses }}</div>
|
||||
<div class="card-label text-muted">Lizenzen Gesamt</div>
|
||||
<div class="chart-container">
|
||||
<canvas id="licenseChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@@ -65,9 +112,9 @@
|
||||
<a href="/sessions" class="text-decoration-none">
|
||||
<div class="card stat-card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">🟢 Sessions</h5>
|
||||
<h2 class="text-success">{{ stats.active_sessions }}</h2>
|
||||
<p class="text-muted mb-0">Aktive Nutzer</p>
|
||||
<div class="card-icon text-success{% if stats.active_sessions > 0 %} pulse-effect{% endif %}">🟢</div>
|
||||
<div class="card-value text-success">{{ stats.active_sessions }}</div>
|
||||
<div class="card-label text-muted">Aktive Sessions</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@@ -119,36 +166,34 @@
|
||||
<!-- Backup-Status und Sicherheit nebeneinander -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">💾 Backup-Status</h5>
|
||||
{% if stats.last_backup %}
|
||||
{% if stats.last_backup[4] == 'success' %}
|
||||
<p class="mb-1">
|
||||
<strong>Letztes Backup:</strong>
|
||||
<span class="text-success">✅ Erfolgreich</span>
|
||||
am {{ stats.last_backup[0].strftime('%d.%m.%Y %H:%M:%S') }}
|
||||
</p>
|
||||
<p class="mb-0">
|
||||
<small class="text-muted">
|
||||
Größe: {{ (stats.last_backup[1] / 1024 / 1024)|round(2) }} MB |
|
||||
Dauer: {{ stats.last_backup[2]|round(1) }} Sekunden |
|
||||
Typ: {{ 'Manuell' if stats.last_backup[3] == 'manual' else 'Automatisch' }}
|
||||
<a href="/backups" class="text-decoration-none">
|
||||
<div class="card stat-card h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">💾 Backup-Status</h5>
|
||||
{% if stats.last_backup %}
|
||||
{% if stats.last_backup[4] == 'success' %}
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<span class="text-success me-2">✅</span>
|
||||
<small>{{ stats.last_backup[0].strftime('%d.%m.%Y %H:%M') }}</small>
|
||||
</div>
|
||||
<div class="progress-custom">
|
||||
<div class="progress-bar-custom" style="width: 100%;"></div>
|
||||
</div>
|
||||
<small class="text-muted mt-1 d-block">
|
||||
{{ (stats.last_backup[1] / 1024 / 1024)|round(1) }} MB • {{ stats.last_backup[2]|round(0)|int }}s
|
||||
</small>
|
||||
</p>
|
||||
{% else %}
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="text-danger me-2">❌</span>
|
||||
<small>Backup fehlgeschlagen</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="mb-0">
|
||||
<strong>Letztes Backup:</strong>
|
||||
<span class="text-danger">❌ Fehlgeschlagen</span>
|
||||
am {{ stats.last_backup[0].strftime('%d.%m.%Y %H:%M:%S') }}
|
||||
</p>
|
||||
<p class="text-muted mb-0">Noch kein Backup vorhanden</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="text-muted mb-0">Noch kein Backup vorhanden</p>
|
||||
{% endif %}
|
||||
<a href="/backups" class="btn btn-sm btn-outline-primary mt-2">Backup-Verwaltung →</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Sicherheitsstatus -->
|
||||
@@ -303,4 +348,46 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
|
||||
<script>
|
||||
// Donut Chart für Lizenzen
|
||||
const ctx = document.getElementById('licenseChart');
|
||||
if (ctx) {
|
||||
const licenseChart = new Chart(ctx, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['Aktiv', 'Abgelaufen'],
|
||||
datasets: [{
|
||||
data: [{{ stats.active_licenses }}, {{ stats.expired_licenses }}],
|
||||
backgroundColor: ['#28a745', '#dc3545'],
|
||||
borderWidth: 0
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
const label = context.label || '';
|
||||
const value = context.parsed || 0;
|
||||
const total = context.dataset.data.reduce((a, b) => a + b, 0);
|
||||
const percentage = ((value / total) * 100).toFixed(1);
|
||||
return label + ': ' + value + ' (' + percentage + '%)';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
cutout: '70%'
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -3,11 +3,6 @@
|
||||
{% block title %}Lizenzübersicht{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.status-aktiv { color: #28a745; }
|
||||
.status-ablaufend { color: #ffc107; }
|
||||
.status-abgelaufen { color: #dc3545; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren