🌟 🎉 Major Update:
BIN
backend/static/css/app-bundle.min.css.gz
Normal file
31
backend/static/css/build/critical.css
Normal file
@ -0,0 +1,31 @@
|
||||
/* CRITICAL INLINE CSS - Sollte im HTML <head> stehen */
|
||||
/* Nur die absolut notwendigen Styles für First Paint */
|
||||
|
||||
:root{--primary:#0073ce;--bg:#fafbfc;--surface:#fff;--text:#111827;--border:#e5e7eb;--shadow:0 2px 4px rgba(0,0,0,.05)}
|
||||
*{box-sizing:border-box;margin:0;padding:0;contain:layout style}
|
||||
body{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--text);line-height:1.5;text-rendering:optimizeSpeed;-webkit-font-smoothing:antialiased}
|
||||
.header{background:var(--surface);border-bottom:1px solid var(--border);padding:1rem;position:sticky;top:0;z-index:1000}
|
||||
.nav{display:flex;gap:1rem}
|
||||
.nav-item{padding:.5rem 1rem;border-radius:6px;text-decoration:none;color:#6b7280;transition:background .1s}
|
||||
.nav-item:hover{background:var(--bg);color:var(--text)}
|
||||
.nav-item.active{background:var(--primary);color:#fff}
|
||||
.container{max-width:1200px;margin:0 auto;padding:0 1rem}
|
||||
.btn{background:var(--primary);color:#fff;border:none;border-radius:6px;padding:.75rem 1.5rem;font-weight:600;cursor:pointer;transition:background .1s}
|
||||
.btn:hover{background:#005a9f}
|
||||
.card{background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:1rem;box-shadow:var(--shadow)}
|
||||
.flex{display:flex}
|
||||
.grid{display:grid;gap:1rem}
|
||||
.hidden{display:none}
|
||||
.w-full{width:100%}
|
||||
.text-center{text-align:center}
|
||||
.p-4{padding:1rem}
|
||||
.mb-4{margin-bottom:1rem}
|
||||
.status{display:inline-block;padding:.25rem .75rem;border-radius:999px;font-size:.75rem;font-weight:600;text-transform:uppercase}
|
||||
.status-online{background:#d1fae5;color:#065f46}
|
||||
.status-offline{background:#fee2e2;color:#991b1b}
|
||||
.status-printing{background:#dbeafe;color:#1e40af}
|
||||
.input{background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:.75rem;width:100%}
|
||||
.input:focus{outline:none;border-color:var(--primary)}
|
||||
@media (prefers-color-scheme:dark){:root{--bg:#1e293b;--surface:#334155;--text:#f8fafc;--border:#475569;--shadow:0 2px 4px rgba(0,0,0,.3)}}
|
||||
@media (max-width:768px){.nav{flex-direction:column;gap:.5rem}}
|
||||
@media (prefers-reduced-motion:reduce){*{transition:none!important}}
|
498
backend/static/css/build/kiosk-1656af86.css
Normal file
@ -0,0 +1,498 @@
|
||||
/* MYP Platform - Kiosk Optimierte CSS Bundle */
|
||||
/* Generiert am: 2025-06-01 23:40:46 */
|
||||
|
||||
/**
|
||||
* MYP Platform - Kiosk-Optimierte CSS
|
||||
* Maximale Performance für Offline-Kiosk-Umgebung
|
||||
* Keine Touch-Events, minimale Animationen, optimierte Rendering-Performance
|
||||
*/
|
||||
|
||||
/* ===== CRITICAL INLINE STYLES (sollten im HTML-Head stehen) ===== */
|
||||
:root {
|
||||
/* Reduzierte Farbvariablen für bessere Performance */
|
||||
--primary: #0073ce;
|
||||
--primary-dark: #005a9f;
|
||||
--bg: #fafbfc;
|
||||
--surface: #ffffff;
|
||||
--text: #111827;
|
||||
--text-muted: #6b7280;
|
||||
--border: #e5e7eb;
|
||||
--shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
/* CSS Containment für bessere Performance */
|
||||
* {
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
/* Basis-Reset ohne aufwendige Normalisierung */
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
line-height: 1.5;
|
||||
contain: layout style paint;
|
||||
/* Optimiert für Kiosk-Rendering */
|
||||
text-rendering: optimizeSpeed;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* ===== LAYOUT OPTIMIERUNGEN ===== */
|
||||
.header {
|
||||
background: var(--surface);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 1rem;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
color: var(--text-muted);
|
||||
transition: background-color 0.1s ease; /* Minimale Transition */
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ===== OPTIMIERTE KARTEN ===== */
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
box-shadow: var(--shadow);
|
||||
contain: layout style paint;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-1px); /* Minimaler Hover-Effekt */
|
||||
}
|
||||
|
||||
/* ===== BUTTONS OHNE KOMPLEXE ANIMATIONEN ===== */
|
||||
.btn {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.1s ease;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
/* ===== INPUTS OPTIMIERT ===== */
|
||||
.input {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
width: 100%;
|
||||
transition: border-color 0.1s ease;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/* ===== TABELLEN OHNE KOMPLEXE EFFEKTE ===== */
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: var(--bg);
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.table td {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.table tr:hover {
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
/* ===== STATUS BADGES VEREINFACHT ===== */
|
||||
.status {
|
||||
display: inline-block;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.status-online { background: #d1fae5; color: #065f46; }
|
||||
.status-offline { background: #fee2e2; color: #991b1b; }
|
||||
.status-printing { background: #dbeafe; color: #1e40af; }
|
||||
|
||||
/* ===== GRID LAYOUTS OPTIMIERT ===== */
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
.grid-2 { grid-template-columns: repeat(2, 1fr); }
|
||||
.grid-3 { grid-template-columns: repeat(3, 1fr); }
|
||||
.grid-4 { grid-template-columns: repeat(4, 1fr); }
|
||||
|
||||
/* ===== UTILITIES MINIMAL ===== */
|
||||
.flex { display: flex; contain: layout; }
|
||||
.flex-col { flex-direction: column; }
|
||||
.items-center { align-items: center; }
|
||||
.justify-between { justify-content: space-between; }
|
||||
.gap-1 { gap: 0.25rem; }
|
||||
.gap-2 { gap: 0.5rem; }
|
||||
.gap-4 { gap: 1rem; }
|
||||
|
||||
.p-1 { padding: 0.25rem; }
|
||||
.p-2 { padding: 0.5rem; }
|
||||
.p-4 { padding: 1rem; }
|
||||
.p-6 { padding: 1.5rem; }
|
||||
|
||||
.m-1 { margin: 0.25rem; }
|
||||
.m-2 { margin: 0.5rem; }
|
||||
.m-4 { margin: 1rem; }
|
||||
|
||||
.mb-2 { margin-bottom: 0.5rem; }
|
||||
.mb-4 { margin-bottom: 1rem; }
|
||||
.mb-6 { margin-bottom: 1.5rem; }
|
||||
|
||||
.text-sm { font-size: 0.875rem; }
|
||||
.text-lg { font-size: 1.125rem; }
|
||||
.text-xl { font-size: 1.25rem; }
|
||||
.text-2xl { font-size: 1.5rem; }
|
||||
|
||||
.font-semibold { font-weight: 600; }
|
||||
.font-bold { font-weight: 700; }
|
||||
|
||||
.text-center { text-align: center; }
|
||||
.text-right { text-align: right; }
|
||||
|
||||
.w-full { width: 100%; }
|
||||
.h-full { height: 100%; }
|
||||
|
||||
.rounded { border-radius: 6px; }
|
||||
.rounded-lg { border-radius: 8px; }
|
||||
|
||||
.border { border: 1px solid var(--border); }
|
||||
.border-t { border-top: 1px solid var(--border); }
|
||||
.border-b { border-bottom: 1px solid var(--border); }
|
||||
|
||||
.bg-white { background: var(--surface); }
|
||||
.bg-gray-50 { background: var(--bg); }
|
||||
|
||||
.text-gray-600 { color: var(--text-muted); }
|
||||
.text-blue-600 { color: var(--primary); }
|
||||
|
||||
/* ===== DARK MODE MINIMAL ===== */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #1e293b;
|
||||
--surface: #334155;
|
||||
--text: #f8fafc;
|
||||
--text-muted: #94a3b8;
|
||||
--border: #475569;
|
||||
--shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE FÜR KIOSK-DISPLAYS ===== */
|
||||
@media (max-width: 1024px) {
|
||||
.grid-4 { grid-template-columns: repeat(2, 1fr); }
|
||||
.grid-3 { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.grid-4,
|
||||
.grid-3,
|
||||
.grid-2 { grid-template-columns: 1fr; }
|
||||
|
||||
.nav {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== PERFORMANCE OPTIMIERUNGEN ===== */
|
||||
/* Deaktivierung nicht benötigter Features für Kiosk */
|
||||
* {
|
||||
/* Keine Touch-Events */
|
||||
touch-action: none;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Nur notwendige Elemente selektierbar */
|
||||
input,
|
||||
textarea {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
/* Optimierte Scrolling-Performance */
|
||||
* {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* GPU-Acceleration nur wo nötig */
|
||||
.card:hover,
|
||||
.btn:hover {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* Minimale Animationen für bessere Performance */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print-Optimierung (falls Kiosk drucken kann) */
|
||||
@media print {
|
||||
.nav,
|
||||
.btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: none;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== LAYOUT SHIFT PREVENTION ===== */
|
||||
img {
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Container für stabile Layouts */
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
/* ===== KIOSK-SPEZIFISCHE OPTIMIERUNGEN ===== */
|
||||
/* Vollbildmodus-Unterstützung */
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Fokus-Management für Keyboard-Navigation */
|
||||
:focus {
|
||||
outline: 2px solid var(--primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Kein Selection-Highlighting */
|
||||
::selection {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Optimierte Font-Loading */
|
||||
@font-face {
|
||||
font-family: 'system-ui';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimales Icon-Set für Kiosk-Modus
|
||||
* SVG-basierte Icons als CSS-Pseudo-Elemente für beste Performance
|
||||
* Ersetzt FontAwesome für deutlich kleinere Bundle-Größe
|
||||
*/
|
||||
|
||||
/* Icon-Basis-Klasse */
|
||||
.icon {
|
||||
display: inline-block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.icon-lg { width: 1.5rem; height: 1.5rem; }
|
||||
.icon-xl { width: 2rem; height: 2rem; }
|
||||
|
||||
/* ===== DRUCKER ICONS ===== */
|
||||
.icon-printer {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-print {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== STATUS ICONS ===== */
|
||||
.icon-check {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2310b981'%3E%3Cpath d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-warning {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23f59e0b'%3E%3Cpath d='M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-error {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ef4444'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-offline {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%236b7280'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== NAVIGATION ICONS ===== */
|
||||
.icon-home {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-settings {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-users {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M16 4c0-1.11.89-2 2-2s2 .89 2 2-.89 2-2 2-2-.89-2-2zm4 18v-6h2.5l-2.54-7.63A3.012 3.012 0 0 0 16.43 6c-.68 0-1.3.27-1.77.72L12 8.5l-2.66-1.78C8.87 6.27 8.25 6 7.57 6c-1.31 0-2.42.83-2.83 2L2.5 16H5v6h2v-6h2v6h2zm-6.5-10.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5S12 9.17 12 10s.67 1.5 1.5 1.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-dashboard {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== ACTION ICONS ===== */
|
||||
.icon-plus {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-edit {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-delete {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ef4444'%3E%3Cpath d='M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-refresh {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== POWER/CONNECTION ICONS ===== */
|
||||
.icon-power {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2310b981'%3E%3Cpath d='M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42C17.99 7.86 19 9.81 19 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.19 1.01-4.14 2.58-5.42L6.17 5.17C4.23 6.82 3 9.26 3 12c0 4.97 4.03 9 9 9s9-4.03 9-9c0-2.74-1.23-5.18-3.17-6.83z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-wifi {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2310b981'%3E%3Cpath d='M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.07 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== DOKUMENT ICONS ===== */
|
||||
.icon-file {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-queue {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== INFO ICONS ===== */
|
||||
.icon-info {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%233b82f6'%3E%3Cpath d='M12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2M13,17H11V11H13M13,9H11V7H13'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-time {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M16.2,16.2L11,13V7H12.5V12.2L17,14.7L16.2,16.2Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== ARROW ICONS ===== */
|
||||
.icon-arrow-right {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-arrow-down {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== HOVER-STATES ===== */
|
||||
.btn:hover .icon,
|
||||
.nav-item:hover .icon {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* ===== DARK MODE ===== */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.icon-printer,
|
||||
.icon-print,
|
||||
.icon-home,
|
||||
.icon-settings,
|
||||
.icon-users,
|
||||
.icon-dashboard,
|
||||
.icon-plus,
|
||||
.icon-edit,
|
||||
.icon-refresh,
|
||||
.icon-file,
|
||||
.icon-queue,
|
||||
.icon-time,
|
||||
.icon-arrow-right,
|
||||
.icon-arrow-down {
|
||||
filter: brightness(2);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE ICON-SIZES ===== */
|
||||
@media (max-width: 768px) {
|
||||
.icon { width: 1.25rem; height: 1.25rem; }
|
||||
.icon-lg { width: 1.75rem; height: 1.75rem; }
|
||||
.icon-xl { width: 2.25rem; height: 2.25rem; }
|
||||
}
|
497
backend/static/css/build/kiosk-no-fa.css
Normal file
@ -0,0 +1,497 @@
|
||||
/* MYP Platform - Kiosk Optimierte CSS Bundle */
|
||||
/* Generiert am: 2025-06-01 23:40:46 */
|
||||
|
||||
/**
|
||||
* MYP Platform - Kiosk-Optimierte CSS
|
||||
* Maximale Performance für Offline-Kiosk-Umgebung
|
||||
* Keine Touch-Events, minimale Animationen, optimierte Rendering-Performance
|
||||
*/
|
||||
|
||||
/* ===== CRITICAL INLINE STYLES (sollten im HTML-Head stehen) ===== */
|
||||
:root {
|
||||
/* Reduzierte Farbvariablen für bessere Performance */
|
||||
--primary: #0073ce;
|
||||
--primary-dark: #005a9f;
|
||||
--bg: #fafbfc;
|
||||
--surface: #ffffff;
|
||||
--text: #111827;
|
||||
--text-muted: #6b7280;
|
||||
--border: #e5e7eb;
|
||||
--shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
/* CSS Containment für bessere Performance */
|
||||
* {
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
/* Basis-Reset ohne aufwendige Normalisierung */
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
line-height: 1.5;
|
||||
contain: layout style paint;
|
||||
/* Optimiert für Kiosk-Rendering */
|
||||
text-rendering: optimizeSpeed;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* ===== LAYOUT OPTIMIERUNGEN ===== */
|
||||
.header {
|
||||
background: var(--surface);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 1rem;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
color: var(--text-muted);
|
||||
transition: background-color 0.1s ease; /* Minimale Transition */
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ===== OPTIMIERTE KARTEN ===== */
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
box-shadow: var(--shadow);
|
||||
contain: layout style paint;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-1px); /* Minimaler Hover-Effekt */
|
||||
}
|
||||
|
||||
/* ===== BUTTONS OHNE KOMPLEXE ANIMATIONEN ===== */
|
||||
.btn {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.1s ease;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
/* ===== INPUTS OPTIMIERT ===== */
|
||||
.input {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
width: 100%;
|
||||
transition: border-color 0.1s ease;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/* ===== TABELLEN OHNE KOMPLEXE EFFEKTE ===== */
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: var(--bg);
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.table td {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.table tr:hover {
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
/* ===== STATUS BADGES VEREINFACHT ===== */
|
||||
.status {
|
||||
display: inline-block;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.status-online { background: #d1fae5; color: #065f46; }
|
||||
.status-offline { background: #fee2e2; color: #991b1b; }
|
||||
.status-printing { background: #dbeafe; color: #1e40af; }
|
||||
|
||||
/* ===== GRID LAYOUTS OPTIMIERT ===== */
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
.grid-2 { grid-template-columns: repeat(2, 1fr); }
|
||||
.grid-3 { grid-template-columns: repeat(3, 1fr); }
|
||||
.grid-4 { grid-template-columns: repeat(4, 1fr); }
|
||||
|
||||
/* ===== UTILITIES MINIMAL ===== */
|
||||
.flex { display: flex; contain: layout; }
|
||||
.flex-col { flex-direction: column; }
|
||||
.items-center { align-items: center; }
|
||||
.justify-between { justify-content: space-between; }
|
||||
.gap-1 { gap: 0.25rem; }
|
||||
.gap-2 { gap: 0.5rem; }
|
||||
.gap-4 { gap: 1rem; }
|
||||
|
||||
.p-1 { padding: 0.25rem; }
|
||||
.p-2 { padding: 0.5rem; }
|
||||
.p-4 { padding: 1rem; }
|
||||
.p-6 { padding: 1.5rem; }
|
||||
|
||||
.m-1 { margin: 0.25rem; }
|
||||
.m-2 { margin: 0.5rem; }
|
||||
.m-4 { margin: 1rem; }
|
||||
|
||||
.mb-2 { margin-bottom: 0.5rem; }
|
||||
.mb-4 { margin-bottom: 1rem; }
|
||||
.mb-6 { margin-bottom: 1.5rem; }
|
||||
|
||||
.text-sm { font-size: 0.875rem; }
|
||||
.text-lg { font-size: 1.125rem; }
|
||||
.text-xl { font-size: 1.25rem; }
|
||||
.text-2xl { font-size: 1.5rem; }
|
||||
|
||||
.font-semibold { font-weight: 600; }
|
||||
.font-bold { font-weight: 700; }
|
||||
|
||||
.text-center { text-align: center; }
|
||||
.text-right { text-align: right; }
|
||||
|
||||
.w-full { width: 100%; }
|
||||
.h-full { height: 100%; }
|
||||
|
||||
.rounded { border-radius: 6px; }
|
||||
.rounded-lg { border-radius: 8px; }
|
||||
|
||||
.border { border: 1px solid var(--border); }
|
||||
.border-t { border-top: 1px solid var(--border); }
|
||||
.border-b { border-bottom: 1px solid var(--border); }
|
||||
|
||||
.bg-white { background: var(--surface); }
|
||||
.bg-gray-50 { background: var(--bg); }
|
||||
|
||||
.text-gray-600 { color: var(--text-muted); }
|
||||
.text-blue-600 { color: var(--primary); }
|
||||
|
||||
/* ===== DARK MODE MINIMAL ===== */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #1e293b;
|
||||
--surface: #334155;
|
||||
--text: #f8fafc;
|
||||
--text-muted: #94a3b8;
|
||||
--border: #475569;
|
||||
--shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE FÜR KIOSK-DISPLAYS ===== */
|
||||
@media (max-width: 1024px) {
|
||||
.grid-4 { grid-template-columns: repeat(2, 1fr); }
|
||||
.grid-3 { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.grid-4,
|
||||
.grid-3,
|
||||
.grid-2 { grid-template-columns: 1fr; }
|
||||
|
||||
.nav {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== PERFORMANCE OPTIMIERUNGEN ===== */
|
||||
/* Deaktivierung nicht benötigter Features für Kiosk */
|
||||
* {
|
||||
/* Keine Touch-Events */
|
||||
touch-action: none;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Nur notwendige Elemente selektierbar */
|
||||
input,
|
||||
textarea {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
/* Optimierte Scrolling-Performance */
|
||||
* {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* GPU-Acceleration nur wo nötig */
|
||||
.card:hover,
|
||||
.btn:hover {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* Minimale Animationen für bessere Performance */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print-Optimierung (falls Kiosk drucken kann) */
|
||||
@media print {
|
||||
.nav,
|
||||
.btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: none;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== LAYOUT SHIFT PREVENTION ===== */
|
||||
img {
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Container für stabile Layouts */
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
/* ===== KIOSK-SPEZIFISCHE OPTIMIERUNGEN ===== */
|
||||
/* Vollbildmodus-Unterstützung */
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Fokus-Management für Keyboard-Navigation */
|
||||
:focus {
|
||||
outline: 2px solid var(--primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Kein Selection-Highlighting */
|
||||
::selection {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Optimierte Font-Loading */
|
||||
@font-face {
|
||||
font-family: 'system-ui';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimales Icon-Set für Kiosk-Modus
|
||||
* SVG-basierte Icons als CSS-Pseudo-Elemente für beste Performance
|
||||
*/
|
||||
|
||||
/* Icon-Basis-Klasse */
|
||||
.icon {
|
||||
display: inline-block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.icon-lg { width: 1.5rem; height: 1.5rem; }
|
||||
.icon-xl { width: 2rem; height: 2rem; }
|
||||
|
||||
/* ===== DRUCKER ICONS ===== */
|
||||
.icon-printer {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-print {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== STATUS ICONS ===== */
|
||||
.icon-check {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2310b981'%3E%3Cpath d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-warning {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23f59e0b'%3E%3Cpath d='M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-error {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ef4444'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-offline {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%236b7280'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== NAVIGATION ICONS ===== */
|
||||
.icon-home {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-settings {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-users {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M16 4c0-1.11.89-2 2-2s2 .89 2 2-.89 2-2 2-2-.89-2-2zm4 18v-6h2.5l-2.54-7.63A3.012 3.012 0 0 0 16.43 6c-.68 0-1.3.27-1.77.72L12 8.5l-2.66-1.78C8.87 6.27 8.25 6 7.57 6c-1.31 0-2.42.83-2.83 2L2.5 16H5v6h2v-6h2v6h2zm-6.5-10.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5S12 9.17 12 10s.67 1.5 1.5 1.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-dashboard {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== ACTION ICONS ===== */
|
||||
.icon-plus {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-edit {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-delete {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ef4444'%3E%3Cpath d='M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-refresh {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== POWER/CONNECTION ICONS ===== */
|
||||
.icon-power {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2310b981'%3E%3Cpath d='M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42C17.99 7.86 19 9.81 19 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.19 1.01-4.14 2.58-5.42L6.17 5.17C4.23 6.82 3 9.26 3 12c0 4.97 4.03 9 9 9s9-4.03 9-9c0-2.74-1.23-5.18-3.17-6.83z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-wifi {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2310b981'%3E%3Cpath d='M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.07 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== DOKUMENT ICONS ===== */
|
||||
.icon-file {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-queue {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== INFO ICONS ===== */
|
||||
.icon-info {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%233b82f6'%3E%3Cpath d='M12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2M13,17H11V11H13M13,9H11V7H13'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-time {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M16.2,16.2L11,13V7H12.5V12.2L17,14.7L16.2,16.2Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== ARROW ICONS ===== */
|
||||
.icon-arrow-right {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-arrow-down {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== HOVER-STATES ===== */
|
||||
.btn:hover .icon,
|
||||
.nav-item:hover .icon {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* ===== DARK MODE ===== */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.icon-printer,
|
||||
.icon-print,
|
||||
.icon-home,
|
||||
.icon-settings,
|
||||
.icon-users,
|
||||
.icon-dashboard,
|
||||
.icon-plus,
|
||||
.icon-edit,
|
||||
.icon-refresh,
|
||||
.icon-file,
|
||||
.icon-queue,
|
||||
.icon-time,
|
||||
.icon-arrow-right,
|
||||
.icon-arrow-down {
|
||||
filter: brightness(2);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE ICON-SIZES ===== */
|
||||
@media (max-width: 768px) {
|
||||
.icon { width: 1.25rem; height: 1.25rem; }
|
||||
.icon-lg { width: 1.75rem; height: 1.75rem; }
|
||||
.icon-xl { width: 2.25rem; height: 2.25rem; }
|
||||
}
|
498
backend/static/css/build/kiosk-production.css
Normal file
@ -0,0 +1,498 @@
|
||||
/* MYP Platform - Kiosk Optimierte CSS Bundle */
|
||||
/* Generiert am: 2025-06-01 23:40:46 */
|
||||
|
||||
/**
|
||||
* MYP Platform - Kiosk-Optimierte CSS
|
||||
* Maximale Performance für Offline-Kiosk-Umgebung
|
||||
* Keine Touch-Events, minimale Animationen, optimierte Rendering-Performance
|
||||
*/
|
||||
|
||||
/* ===== CRITICAL INLINE STYLES (sollten im HTML-Head stehen) ===== */
|
||||
:root {
|
||||
/* Reduzierte Farbvariablen für bessere Performance */
|
||||
--primary: #0073ce;
|
||||
--primary-dark: #005a9f;
|
||||
--bg: #fafbfc;
|
||||
--surface: #ffffff;
|
||||
--text: #111827;
|
||||
--text-muted: #6b7280;
|
||||
--border: #e5e7eb;
|
||||
--shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
/* CSS Containment für bessere Performance */
|
||||
* {
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
/* Basis-Reset ohne aufwendige Normalisierung */
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
line-height: 1.5;
|
||||
contain: layout style paint;
|
||||
/* Optimiert für Kiosk-Rendering */
|
||||
text-rendering: optimizeSpeed;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* ===== LAYOUT OPTIMIERUNGEN ===== */
|
||||
.header {
|
||||
background: var(--surface);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 1rem;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
color: var(--text-muted);
|
||||
transition: background-color 0.1s ease; /* Minimale Transition */
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ===== OPTIMIERTE KARTEN ===== */
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
box-shadow: var(--shadow);
|
||||
contain: layout style paint;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-1px); /* Minimaler Hover-Effekt */
|
||||
}
|
||||
|
||||
/* ===== BUTTONS OHNE KOMPLEXE ANIMATIONEN ===== */
|
||||
.btn {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.1s ease;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
/* ===== INPUTS OPTIMIERT ===== */
|
||||
.input {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
width: 100%;
|
||||
transition: border-color 0.1s ease;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/* ===== TABELLEN OHNE KOMPLEXE EFFEKTE ===== */
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: var(--bg);
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.table td {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.table tr:hover {
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
/* ===== STATUS BADGES VEREINFACHT ===== */
|
||||
.status {
|
||||
display: inline-block;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.status-online { background: #d1fae5; color: #065f46; }
|
||||
.status-offline { background: #fee2e2; color: #991b1b; }
|
||||
.status-printing { background: #dbeafe; color: #1e40af; }
|
||||
|
||||
/* ===== GRID LAYOUTS OPTIMIERT ===== */
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
.grid-2 { grid-template-columns: repeat(2, 1fr); }
|
||||
.grid-3 { grid-template-columns: repeat(3, 1fr); }
|
||||
.grid-4 { grid-template-columns: repeat(4, 1fr); }
|
||||
|
||||
/* ===== UTILITIES MINIMAL ===== */
|
||||
.flex { display: flex; contain: layout; }
|
||||
.flex-col { flex-direction: column; }
|
||||
.items-center { align-items: center; }
|
||||
.justify-between { justify-content: space-between; }
|
||||
.gap-1 { gap: 0.25rem; }
|
||||
.gap-2 { gap: 0.5rem; }
|
||||
.gap-4 { gap: 1rem; }
|
||||
|
||||
.p-1 { padding: 0.25rem; }
|
||||
.p-2 { padding: 0.5rem; }
|
||||
.p-4 { padding: 1rem; }
|
||||
.p-6 { padding: 1.5rem; }
|
||||
|
||||
.m-1 { margin: 0.25rem; }
|
||||
.m-2 { margin: 0.5rem; }
|
||||
.m-4 { margin: 1rem; }
|
||||
|
||||
.mb-2 { margin-bottom: 0.5rem; }
|
||||
.mb-4 { margin-bottom: 1rem; }
|
||||
.mb-6 { margin-bottom: 1.5rem; }
|
||||
|
||||
.text-sm { font-size: 0.875rem; }
|
||||
.text-lg { font-size: 1.125rem; }
|
||||
.text-xl { font-size: 1.25rem; }
|
||||
.text-2xl { font-size: 1.5rem; }
|
||||
|
||||
.font-semibold { font-weight: 600; }
|
||||
.font-bold { font-weight: 700; }
|
||||
|
||||
.text-center { text-align: center; }
|
||||
.text-right { text-align: right; }
|
||||
|
||||
.w-full { width: 100%; }
|
||||
.h-full { height: 100%; }
|
||||
|
||||
.rounded { border-radius: 6px; }
|
||||
.rounded-lg { border-radius: 8px; }
|
||||
|
||||
.border { border: 1px solid var(--border); }
|
||||
.border-t { border-top: 1px solid var(--border); }
|
||||
.border-b { border-bottom: 1px solid var(--border); }
|
||||
|
||||
.bg-white { background: var(--surface); }
|
||||
.bg-gray-50 { background: var(--bg); }
|
||||
|
||||
.text-gray-600 { color: var(--text-muted); }
|
||||
.text-blue-600 { color: var(--primary); }
|
||||
|
||||
/* ===== DARK MODE MINIMAL ===== */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #1e293b;
|
||||
--surface: #334155;
|
||||
--text: #f8fafc;
|
||||
--text-muted: #94a3b8;
|
||||
--border: #475569;
|
||||
--shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE FÜR KIOSK-DISPLAYS ===== */
|
||||
@media (max-width: 1024px) {
|
||||
.grid-4 { grid-template-columns: repeat(2, 1fr); }
|
||||
.grid-3 { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.grid-4,
|
||||
.grid-3,
|
||||
.grid-2 { grid-template-columns: 1fr; }
|
||||
|
||||
.nav {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== PERFORMANCE OPTIMIERUNGEN ===== */
|
||||
/* Deaktivierung nicht benötigter Features für Kiosk */
|
||||
* {
|
||||
/* Keine Touch-Events */
|
||||
touch-action: none;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Nur notwendige Elemente selektierbar */
|
||||
input,
|
||||
textarea {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
/* Optimierte Scrolling-Performance */
|
||||
* {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* GPU-Acceleration nur wo nötig */
|
||||
.card:hover,
|
||||
.btn:hover {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* Minimale Animationen für bessere Performance */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print-Optimierung (falls Kiosk drucken kann) */
|
||||
@media print {
|
||||
.nav,
|
||||
.btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: none;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== LAYOUT SHIFT PREVENTION ===== */
|
||||
img {
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Container für stabile Layouts */
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
/* ===== KIOSK-SPEZIFISCHE OPTIMIERUNGEN ===== */
|
||||
/* Vollbildmodus-Unterstützung */
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Fokus-Management für Keyboard-Navigation */
|
||||
:focus {
|
||||
outline: 2px solid var(--primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Kein Selection-Highlighting */
|
||||
::selection {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Optimierte Font-Loading */
|
||||
@font-face {
|
||||
font-family: 'system-ui';
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimales Icon-Set für Kiosk-Modus
|
||||
* SVG-basierte Icons als CSS-Pseudo-Elemente für beste Performance
|
||||
* Ersetzt FontAwesome für deutlich kleinere Bundle-Größe
|
||||
*/
|
||||
|
||||
/* Icon-Basis-Klasse */
|
||||
.icon {
|
||||
display: inline-block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.icon-lg { width: 1.5rem; height: 1.5rem; }
|
||||
.icon-xl { width: 2rem; height: 2rem; }
|
||||
|
||||
/* ===== DRUCKER ICONS ===== */
|
||||
.icon-printer {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-print {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== STATUS ICONS ===== */
|
||||
.icon-check {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2310b981'%3E%3Cpath d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-warning {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23f59e0b'%3E%3Cpath d='M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-error {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ef4444'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-offline {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%236b7280'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== NAVIGATION ICONS ===== */
|
||||
.icon-home {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-settings {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-users {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M16 4c0-1.11.89-2 2-2s2 .89 2 2-.89 2-2 2-2-.89-2-2zm4 18v-6h2.5l-2.54-7.63A3.012 3.012 0 0 0 16.43 6c-.68 0-1.3.27-1.77.72L12 8.5l-2.66-1.78C8.87 6.27 8.25 6 7.57 6c-1.31 0-2.42.83-2.83 2L2.5 16H5v6h2v-6h2v6h2zm-6.5-10.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5S12 9.17 12 10s.67 1.5 1.5 1.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-dashboard {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== ACTION ICONS ===== */
|
||||
.icon-plus {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-edit {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-delete {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ef4444'%3E%3Cpath d='M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-refresh {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== POWER/CONNECTION ICONS ===== */
|
||||
.icon-power {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2310b981'%3E%3Cpath d='M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42C17.99 7.86 19 9.81 19 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.19 1.01-4.14 2.58-5.42L6.17 5.17C4.23 6.82 3 9.26 3 12c0 4.97 4.03 9 9 9s9-4.03 9-9c0-2.74-1.23-5.18-3.17-6.83z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-wifi {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2310b981'%3E%3Cpath d='M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.07 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== DOKUMENT ICONS ===== */
|
||||
.icon-file {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-queue {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== INFO ICONS ===== */
|
||||
.icon-info {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%233b82f6'%3E%3Cpath d='M12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2M13,17H11V11H13M13,9H11V7H13'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-time {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M16.2,16.2L11,13V7H12.5V12.2L17,14.7L16.2,16.2Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== ARROW ICONS ===== */
|
||||
.icon-arrow-right {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-arrow-down {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== HOVER-STATES ===== */
|
||||
.btn:hover .icon,
|
||||
.nav-item:hover .icon {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* ===== DARK MODE ===== */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.icon-printer,
|
||||
.icon-print,
|
||||
.icon-home,
|
||||
.icon-settings,
|
||||
.icon-users,
|
||||
.icon-dashboard,
|
||||
.icon-plus,
|
||||
.icon-edit,
|
||||
.icon-refresh,
|
||||
.icon-file,
|
||||
.icon-queue,
|
||||
.icon-time,
|
||||
.icon-arrow-right,
|
||||
.icon-arrow-down {
|
||||
filter: brightness(2);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE ICON-SIZES ===== */
|
||||
@media (max-width: 768px) {
|
||||
.icon { width: 1.25rem; height: 1.25rem; }
|
||||
.icon-lg { width: 1.75rem; height: 1.75rem; }
|
||||
.icon-xl { width: 2.25rem; height: 2.25rem; }
|
||||
}
|
288
backend/static/css/caching-optimizations.css
Normal file
@ -0,0 +1,288 @@
|
||||
/**
|
||||
* MYP Platform - CSS Caching-Optimierungen
|
||||
* Performance-optimierte Styles für besseres Caching und schnellere Ladezeiten
|
||||
*/
|
||||
|
||||
/* ===== KRITISCHE ABOVE-THE-FOLD STYLES ===== */
|
||||
/* Diese Styles sollten inline im HTML-Head geladen werden */
|
||||
.critical-header {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(8px);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.critical-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.critical-logo {
|
||||
font-weight: 700;
|
||||
font-size: 1.25rem;
|
||||
color: #0073ce;
|
||||
}
|
||||
|
||||
.critical-main {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #fafbfc 0%, #f5f7f9 100%);
|
||||
}
|
||||
|
||||
/* ===== LAZY LOADING PLACEHOLDER ===== */
|
||||
.lazy-placeholder {
|
||||
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-loading 1.5s infinite ease-in-out;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@keyframes skeleton-loading {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dark .lazy-placeholder {
|
||||
background: linear-gradient(90deg, #374151 25%, #4b5563 50%, #374151 75%);
|
||||
background-size: 200% 100%;
|
||||
}
|
||||
|
||||
/* ===== PRELOAD HINTS ===== */
|
||||
.preload-image {
|
||||
content-visibility: auto;
|
||||
contain-intrinsic-size: 300px 200px;
|
||||
}
|
||||
|
||||
.preload-component {
|
||||
content-visibility: auto;
|
||||
contain-intrinsic-size: 100px;
|
||||
}
|
||||
|
||||
/* ===== CACHE-OPTIMIERTE KOMPONENTEN ===== */
|
||||
.cache-card {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border: 1px solid rgba(229, 231, 235, 0.5);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
transition: transform 0.2s ease;
|
||||
contain: layout style paint;
|
||||
}
|
||||
|
||||
.cache-card:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.cache-button {
|
||||
background: #0073ce;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 0.5rem 1rem;
|
||||
font-weight: 600;
|
||||
transition: background-color 0.2s ease;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.cache-button:hover {
|
||||
background: #005a9f;
|
||||
}
|
||||
|
||||
.cache-input {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border: 1px solid rgba(229, 231, 235, 0.8);
|
||||
border-radius: 4px;
|
||||
padding: 0.5rem;
|
||||
transition: border-color 0.2s ease;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.cache-input:focus {
|
||||
outline: none;
|
||||
border-color: #0073ce;
|
||||
box-shadow: 0 0 0 2px rgba(0, 115, 206, 0.1);
|
||||
}
|
||||
|
||||
/* ===== LAYOUT SHIFT PREVENTION ===== */
|
||||
.prevent-cls {
|
||||
min-height: 200px; /* Prevent layout shift */
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
.image-container {
|
||||
aspect-ratio: 16/9;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
background: #f3f4f6;
|
||||
}
|
||||
|
||||
.image-container img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.image-container img[loading="lazy"] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.image-container img[loading="lazy"].loaded {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ===== OPTIMIERTE TYPOGRAPHY ===== */
|
||||
.text-heading {
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
color: #111827;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.text-body {
|
||||
line-height: 1.6;
|
||||
color: #374151;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #6b7280;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
/* ===== DARK MODE CACHE OPTIMIZATIONS ===== */
|
||||
.dark .cache-card {
|
||||
background: rgba(30, 41, 59, 0.9);
|
||||
border-color: rgba(100, 116, 139, 0.3);
|
||||
}
|
||||
|
||||
.dark .cache-input {
|
||||
background: rgba(30, 41, 59, 0.9);
|
||||
border-color: rgba(100, 116, 139, 0.5);
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.dark .text-heading {
|
||||
color: #f8fafc;
|
||||
}
|
||||
|
||||
.dark .text-body {
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.dark .text-muted {
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.dark .image-container {
|
||||
background: #374151;
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE OPTIMIZATIONS ===== */
|
||||
@media (max-width: 768px) {
|
||||
.critical-nav {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.cache-card {
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.cache-button {
|
||||
padding: 0.75rem 1.25rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== PRINT OPTIMIZATIONS ===== */
|
||||
@media print {
|
||||
.critical-header,
|
||||
.cache-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cache-card {
|
||||
box-shadow: none;
|
||||
border: 1px solid #000;
|
||||
break-inside: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== GPU ACCELERATION HINTS ===== */
|
||||
.gpu-accelerated {
|
||||
transform: translateZ(0);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* ===== EFFICIENT ANIMATIONS ===== */
|
||||
.fade-in-optimized {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-in-optimized.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* ===== CONTAINER QUERIES SUPPORT ===== */
|
||||
@container (min-width: 300px) {
|
||||
.cache-card {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== CONTENT VISIBILITY OPTIMIZATIONS ===== */
|
||||
.auto-height {
|
||||
content-visibility: auto;
|
||||
contain-intrinsic-size: 0 200px;
|
||||
}
|
||||
|
||||
.hidden-content {
|
||||
content-visibility: hidden;
|
||||
}
|
||||
|
||||
/* ===== REDUCED MOTION PREFERENCES ===== */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.fade-in-optimized,
|
||||
.cache-card,
|
||||
.cache-button,
|
||||
.cache-input {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.lazy-placeholder {
|
||||
animation: none !important;
|
||||
background: #f0f0f0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== HIGH CONTRAST MODE ===== */
|
||||
@media (prefers-contrast: high) {
|
||||
.cache-card {
|
||||
border-width: 2px;
|
||||
border-color: #000;
|
||||
}
|
||||
|
||||
.cache-button {
|
||||
border: 2px solid #000;
|
||||
}
|
||||
|
||||
.cache-input {
|
||||
border-width: 2px;
|
||||
border-color: #000;
|
||||
}
|
||||
}
|
BIN
backend/static/css/caching-optimizations.css.gz
Normal file
1
backend/static/css/caching-optimizations.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.critical-header{background:rgba(255,255,255,0.95);backdrop-filter:blur(8px);position:sticky;top:0;z-index:1000;box-shadow:0 2px 8px rgba(0,0,0,0.06)}.critical-nav{display:flex;align-items:center;padding:1rem;max-width:1200px;margin:0 auto}.critical-logo{font-weight:700;font-size:1.25rem;color:#0073ce}.critical-main{min-height:100vh;background:linear-gradient(135deg,#fafbfc 0,#f5f7f9 100%)}.lazy-placeholder{background:linear-gradient(90deg,#f0f0f0 25%,#e0e0e0 50%,#f0f0f0 75%);background-size:200% 100%;animation:skeleton-loading 1.5s infinite ease-in-out;border-radius:4px}@keyframes skeleton-loading{0%{background-position:200% 0}100%{background-position:-200% 0}}.dark .lazy-placeholder{background:linear-gradient(90deg,#374151 25%,#4b5563 50%,#374151 75%);background-size:200% 100%}.preload-image{content-visibility:auto;contain-intrinsic-size:300px 200px}.preload-component{content-visibility:auto;contain-intrinsic-size:100px}.cache-card{background:rgba(255,255,255,0.9);border:1px solid rgba(229,231,235,0.5);border-radius:8px;padding:1rem;margin-bottom:1rem;transition:transform .2s ease;contain:layout style paint}.cache-card:hover{transform:translateY(-1px)}.cache-button{background:#0073ce;color:white;border:0;border-radius:6px;padding:.5rem 1rem;font-weight:600;transition:background-color .2s ease;contain:layout style}.cache-button:hover{background:#005a9f}.cache-input{background:rgba(255,255,255,0.9);border:1px solid rgba(229,231,235,0.8);border-radius:4px;padding:.5rem;transition:border-color .2s ease;contain:layout style}.cache-input:focus{outline:0;border-color:#0073ce;box-shadow:0 0 0 2px rgba(0,115,206,0.1)}.prevent-cls{min-height:200px;contain:layout}.image-container{aspect-ratio:16/9;overflow:hidden;border-radius:8px;background:#f3f4f6}.image-container img{width:100%;height:100%;object-fit:cover;transition:opacity .3s ease}.image-container img[loading="lazy"]{opacity:0}.image-container img[loading="lazy"].loaded{opacity:1}.text-heading{font-weight:700;line-height:1.2;color:#111827;contain:layout style}.text-body{line-height:1.6;color:#374151;contain:layout style}.text-muted{color:#6b7280;contain:layout style}.dark .cache-card{background:rgba(30,41,59,0.9);border-color:rgba(100,116,139,0.3)}.dark .cache-input{background:rgba(30,41,59,0.9);border-color:rgba(100,116,139,0.5);color:#e2e8f0}.dark .text-heading{color:#f8fafc}.dark .text-body{color:#e2e8f0}.dark .text-muted{color:#94a3b8}.dark .image-container{background:#374151}@media(max-width:768px){.critical-nav{padding:.75rem}.cache-card{padding:.75rem;margin-bottom:.75rem}.cache-button{padding:.75rem 1.25rem;font-size:.875rem}}@media print{.critical-header,.cache-button{display:none}.cache-card{box-shadow:none;border:1px solid #000;break-inside:avoid}}.gpu-accelerated{transform:translateZ(0);will-change:transform}.fade-in-optimized{opacity:0;transform:translateY(10px);transition:opacity .3s ease,transform .3s ease}.fade-in-optimized.visible{opacity:1;transform:translateY(0)}@container(min-width:300px){.cache-card{display:grid;grid-template-columns:1fr auto;gap:1rem;align-items:center}}.auto-height{content-visibility:auto;contain-intrinsic-size:0 200px}.hidden-content{content-visibility:hidden}@media(prefers-reduced-motion:reduce){.fade-in-optimized,.cache-card,.cache-button,.cache-input{transition:none !important}.lazy-placeholder{animation:none !important;background:#f0f0f0 !important}}@media(prefers-contrast:high){.cache-card{border-width:2px;border-color:#000}.cache-button{border:2px solid #000}.cache-input{border-width:2px;border-color:#000}}
|
BIN
backend/static/css/caching-optimizations.min.css.gz
Normal file
BIN
backend/static/css/components.css.gz
Normal file
1
backend/static/css/components.min.css
vendored
Normal file
BIN
backend/static/css/components.min.css.gz
Normal file
31
backend/static/css/critical-inline.css
Normal file
@ -0,0 +1,31 @@
|
||||
/* CRITICAL INLINE CSS - Sollte im HTML <head> stehen */
|
||||
/* Nur die absolut notwendigen Styles für First Paint */
|
||||
|
||||
:root{--primary:#0073ce;--bg:#fafbfc;--surface:#fff;--text:#111827;--border:#e5e7eb;--shadow:0 2px 4px rgba(0,0,0,.05)}
|
||||
*{box-sizing:border-box;margin:0;padding:0;contain:layout style}
|
||||
body{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--text);line-height:1.5;text-rendering:optimizeSpeed;-webkit-font-smoothing:antialiased}
|
||||
.header{background:var(--surface);border-bottom:1px solid var(--border);padding:1rem;position:sticky;top:0;z-index:1000}
|
||||
.nav{display:flex;gap:1rem}
|
||||
.nav-item{padding:.5rem 1rem;border-radius:6px;text-decoration:none;color:#6b7280;transition:background .1s}
|
||||
.nav-item:hover{background:var(--bg);color:var(--text)}
|
||||
.nav-item.active{background:var(--primary);color:#fff}
|
||||
.container{max-width:1200px;margin:0 auto;padding:0 1rem}
|
||||
.btn{background:var(--primary);color:#fff;border:none;border-radius:6px;padding:.75rem 1.5rem;font-weight:600;cursor:pointer;transition:background .1s}
|
||||
.btn:hover{background:#005a9f}
|
||||
.card{background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:1rem;box-shadow:var(--shadow)}
|
||||
.flex{display:flex}
|
||||
.grid{display:grid;gap:1rem}
|
||||
.hidden{display:none}
|
||||
.w-full{width:100%}
|
||||
.text-center{text-align:center}
|
||||
.p-4{padding:1rem}
|
||||
.mb-4{margin-bottom:1rem}
|
||||
.status{display:inline-block;padding:.25rem .75rem;border-radius:999px;font-size:.75rem;font-weight:600;text-transform:uppercase}
|
||||
.status-online{background:#d1fae5;color:#065f46}
|
||||
.status-offline{background:#fee2e2;color:#991b1b}
|
||||
.status-printing{background:#dbeafe;color:#1e40af}
|
||||
.input{background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:.75rem;width:100%}
|
||||
.input:focus{outline:none;border-color:var(--primary)}
|
||||
@media (prefers-color-scheme:dark){:root{--bg:#1e293b;--surface:#334155;--text:#f8fafc;--border:#475569;--shadow:0 2px 4px rgba(0,0,0,.3)}}
|
||||
@media (max-width:768px){.nav{flex-direction:column;gap:.5rem}}
|
||||
@media (prefers-reduced-motion:reduce){*{transition:none!important}}
|
BIN
backend/static/css/critical-inline.css.gz
Normal file
1
backend/static/css/critical-inline.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
*,*::before,*::after{box-sizing:border-box}body{margin:0;font-family:system-ui,-apple-system,sans-serif;line-height:1.6;color:#111827;background:#fafbfc}.critical-header{position:sticky;top:0;z-index:1000;background:rgba(255,255,255,0.95);backdrop-filter:blur(8px);border-bottom:1px solid #e5e7eb}.critical-nav{display:flex;align-items:center;justify-content:space-between;padding:1rem;max-width:1200px;margin:0 auto}.critical-logo{font-weight:700;font-size:1.25rem;color:#0073ce;text-decoration:none}.critical-main{min-height:100vh;padding:2rem 1rem}.critical-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:1.5rem;max-width:1200px;margin:0 auto}.critical-card{background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:1.5rem;box-shadow:0 1px 3px rgba(0,0,0,0.1)}.btn-primary{background:#0073ce;color:#fff;border:0;border-radius:6px;padding:.75rem 1.5rem;font-weight:600;cursor:pointer;text-decoration:none;display:inline-block}.loading{opacity:.6;pointer-events:none}.hidden{display:none}@media(max-width:768px){.critical-nav{padding:.75rem}.critical-main{padding:1rem}.critical-grid{grid-template-columns:1fr}.critical-card{padding:1rem}}@media(prefers-color-scheme:dark){body{background:#0f172a;color:#f8fafc}.critical-header{background:rgba(15,23,42,0.95);border-bottom-color:#334155}.critical-card{background:#1e293b;border-color:#334155;color:#f8fafc}}
|
BIN
backend/static/css/critical-inline.min.css.gz
Normal file
@ -1,485 +1,255 @@
|
||||
/* Enhanced Glassmorphism Effects for MYP Application */
|
||||
/* Vereinfachte Glassmorphism-Effekte für MYP Application */
|
||||
|
||||
/* Base Glass Effects */
|
||||
/* ===== BASIS GLASS EFFEKTE ===== */
|
||||
.glass-base {
|
||||
backdrop-filter: blur(24px) saturate(200%) brightness(115%);
|
||||
-webkit-backdrop-filter: blur(24px) saturate(200%) brightness(115%);
|
||||
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||
box-shadow:
|
||||
0 25px 50px rgba(0, 0, 0, 0.12),
|
||||
0 8px 16px rgba(0, 0, 0, 0.08),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.glass-strong {
|
||||
backdrop-filter: blur(28px) saturate(220%) brightness(125%);
|
||||
-webkit-backdrop-filter: blur(28px) saturate(220%) brightness(125%);
|
||||
box-shadow:
|
||||
0 35px 70px rgba(0, 0, 0, 0.15),
|
||||
0 12px 24px rgba(0, 0, 0, 0.1),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.4);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.glass-subtle {
|
||||
backdrop-filter: blur(20px) saturate(180%) brightness(110%);
|
||||
-webkit-backdrop-filter: blur(20px) saturate(180%) brightness(110%);
|
||||
box-shadow:
|
||||
0 15px 35px rgba(0, 0, 0, 0.08),
|
||||
0 4px 8px rgba(0, 0, 0, 0.05),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* Light Mode Glass */
|
||||
.glass-light {
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
box-shadow:
|
||||
0 20px 40px rgba(0, 0, 0, 0.1),
|
||||
0 8px 16px rgba(0, 115, 206, 0.05),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.glass-light-strong {
|
||||
background: rgba(255, 255, 255, 0.75);
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
box-shadow:
|
||||
0 25px 50px rgba(0, 0, 0, 0.12),
|
||||
0 10px 20px rgba(0, 115, 206, 0.08),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
/* Light Mode Glass Premium */
|
||||
.glass-light-premium {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 255, 255, 0.9) 0%,
|
||||
rgba(248, 250, 252, 0.8) 50%,
|
||||
rgba(255, 255, 255, 0.85) 100%);
|
||||
border: 1px solid rgba(226, 232, 240, 0.6);
|
||||
box-shadow:
|
||||
0 25px 50px rgba(0, 0, 0, 0.08),
|
||||
0 10px 20px rgba(0, 115, 206, 0.06),
|
||||
inset 0 2px 0 rgba(255, 255, 255, 0.8),
|
||||
inset 0 0 20px rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.glass-light-card {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 255, 255, 0.95) 0%,
|
||||
rgba(250, 251, 252, 0.9) 100%);
|
||||
border: 1px solid rgba(226, 232, 240, 0.4);
|
||||
box-shadow:
|
||||
0 20px 40px rgba(0, 0, 0, 0.06),
|
||||
0 8px 16px rgba(0, 115, 206, 0.04),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
/* Dark Mode Glass */
|
||||
.glass-dark {
|
||||
/* ===== DARK MODE GLASS ===== */
|
||||
.dark .glass-base {
|
||||
background: rgba(15, 23, 42, 0.8);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow:
|
||||
0 25px 50px rgba(0, 0, 0, 0.4),
|
||||
0 8px 16px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.glass-dark-strong {
|
||||
.dark .glass-strong {
|
||||
background: rgba(30, 41, 59, 0.85);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
box-shadow:
|
||||
0 35px 70px rgba(0, 0, 0, 0.5),
|
||||
0 12px 24px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
/* Interactive Glass Elements */
|
||||
.glass-interactive {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
.dark .glass-subtle {
|
||||
background: rgba(15, 23, 42, 0.7);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.glass-interactive::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.2),
|
||||
transparent);
|
||||
transition: left 0.6s ease;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.glass-interactive:hover {
|
||||
transform: translateY(-3px) scale(1.01);
|
||||
backdrop-filter: blur(32px) saturate(240%) brightness(130%);
|
||||
-webkit-backdrop-filter: blur(32px) saturate(240%) brightness(130%);
|
||||
box-shadow:
|
||||
0 40px 80px rgba(0, 0, 0, 0.15),
|
||||
0 16px 32px rgba(0, 115, 206, 0.1),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.glass-interactive:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.glass-interactive:active {
|
||||
transform: translateY(-1px) scale(0.99);
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
|
||||
/* Glass Navigation */
|
||||
.glass-nav {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 255, 255, 0.9) 0%,
|
||||
rgba(248, 250, 252, 0.85) 50%,
|
||||
rgba(255, 255, 255, 0.9) 100%);
|
||||
backdrop-filter: blur(24px) saturate(200%) brightness(115%);
|
||||
-webkit-backdrop-filter: blur(24px) saturate(200%) brightness(115%);
|
||||
border: 1px solid rgba(226, 232, 240, 0.5);
|
||||
border-bottom: 1px solid rgba(203, 213, 225, 0.6);
|
||||
box-shadow:
|
||||
0 8px 32px rgba(0, 0, 0, 0.08),
|
||||
0 4px 12px rgba(0, 115, 206, 0.05),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.dark .glass-nav {
|
||||
background: rgba(15, 23, 42, 0.85);
|
||||
border-color: rgba(51, 65, 85, 0.6);
|
||||
border-bottom-color: rgba(71, 85, 105, 0.7);
|
||||
box-shadow:
|
||||
0 8px 32px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Glass Cards */
|
||||
/* ===== GLASS KARTEN ===== */
|
||||
.glass-card {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 255, 255, 0.95) 0%,
|
||||
rgba(250, 251, 252, 0.9) 100%);
|
||||
backdrop-filter: blur(20px) saturate(180%) brightness(110%);
|
||||
-webkit-backdrop-filter: blur(20px) saturate(180%) brightness(110%);
|
||||
border: 1px solid rgba(229, 231, 235, 0.6);
|
||||
border-radius: 16px;
|
||||
box-shadow:
|
||||
0 20px 40px rgba(0, 0, 0, 0.08),
|
||||
0 8px 16px rgba(0, 115, 206, 0.04),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(229, 231, 235, 0.5);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08);
|
||||
padding: 1.5rem;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glass-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
rgba(226, 232, 240, 0.8) 50%,
|
||||
transparent 100%);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.glass-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 25px 50px rgba(0, 0, 0, 0.12),
|
||||
0 12px 24px rgba(0, 115, 206, 0.08),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.95);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 12px 20px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.dark .glass-card {
|
||||
background: rgba(30, 41, 59, 0.85);
|
||||
border-color: rgba(100, 116, 139, 0.4);
|
||||
box-shadow:
|
||||
0 20px 40px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(100, 116, 139, 0.3);
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.dark .glass-card:hover {
|
||||
box-shadow:
|
||||
0 25px 50px rgba(0, 0, 0, 0.4),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.15);
|
||||
box-shadow: 0 12px 20px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
/* Glass Buttons */
|
||||
/* ===== GLASS NAVIGATION ===== */
|
||||
.glass-nav {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(226, 232, 240, 0.4);
|
||||
border-bottom: 1px solid rgba(203, 213, 225, 0.5);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.dark .glass-nav {
|
||||
background: rgba(15, 23, 42, 0.85);
|
||||
border: 1px solid rgba(51, 65, 85, 0.5);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* ===== GLASS BUTTONS ===== */
|
||||
.glass-btn {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 255, 255, 0.9) 0%,
|
||||
rgba(248, 250, 252, 0.8) 100%);
|
||||
backdrop-filter: blur(16px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(16px) saturate(180%);
|
||||
border: 1px solid rgba(226, 232, 240, 0.6);
|
||||
border-radius: 12px;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(226, 232, 240, 0.5);
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem 1.5rem;
|
||||
color: #0f172a;
|
||||
font-weight: 600;
|
||||
text-shadow: 0 1px 2px rgba(255, 255, 255, 0.8);
|
||||
box-shadow:
|
||||
0 4px 12px rgba(0, 0, 0, 0.08),
|
||||
0 2px 4px rgba(0, 115, 206, 0.05),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
transition: transform 0.2s ease, background 0.2s ease;
|
||||
}
|
||||
|
||||
.glass-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 255, 255, 0.95) 0%,
|
||||
rgba(248, 250, 252, 0.85) 100%);
|
||||
box-shadow:
|
||||
0 8px 20px rgba(0, 0, 0, 0.12),
|
||||
0 4px 8px rgba(0, 115, 206, 0.08),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.glass-btn:active {
|
||||
transform: translateY(0);
|
||||
box-shadow:
|
||||
0 2px 8px rgba(0, 0, 0, 0.1),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.7);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.glass-btn-primary {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(0, 115, 206, 0.9) 0%,
|
||||
rgba(0, 90, 159, 0.85) 100%);
|
||||
background: rgba(0, 115, 206, 0.9);
|
||||
color: white;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
box-shadow:
|
||||
0 4px 12px rgba(0, 115, 206, 0.3),
|
||||
0 2px 4px rgba(0, 115, 206, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 2px 8px rgba(0, 115, 206, 0.2);
|
||||
}
|
||||
|
||||
.glass-btn-primary:hover {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(0, 115, 206, 0.95) 0%,
|
||||
rgba(0, 90, 159, 0.9) 100%);
|
||||
box-shadow:
|
||||
0 8px 20px rgba(0, 115, 206, 0.4),
|
||||
0 4px 8px rgba(0, 115, 206, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
background: rgba(0, 115, 206, 0.95);
|
||||
box-shadow: 0 4px 12px rgba(0, 115, 206, 0.3);
|
||||
}
|
||||
|
||||
.dark .glass-btn {
|
||||
background: rgba(30, 41, 59, 0.8);
|
||||
color: #e2e8f0;
|
||||
border-color: rgba(100, 116, 139, 0.6);
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
|
||||
box-shadow:
|
||||
0 4px 12px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(100, 116, 139, 0.5);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* Glass Modals */
|
||||
.dark .glass-btn:hover {
|
||||
background: rgba(30, 41, 59, 0.9);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
/* ===== GLASS MODALS ===== */
|
||||
.glass-modal {
|
||||
background: linear-gradient(135deg,
|
||||
rgba(255, 255, 255, 0.98) 0%,
|
||||
rgba(248, 250, 252, 0.95) 50%,
|
||||
rgba(255, 255, 255, 0.98) 100%);
|
||||
backdrop-filter: blur(32px) saturate(220%) brightness(120%);
|
||||
-webkit-backdrop-filter: blur(32px) saturate(220%) brightness(120%);
|
||||
border: 1px solid rgba(226, 232, 240, 0.7);
|
||||
border-radius: 20px;
|
||||
box-shadow:
|
||||
0 50px 100px rgba(0, 0, 0, 0.15),
|
||||
0 20px 40px rgba(0, 115, 206, 0.08),
|
||||
inset 0 2px 0 rgba(255, 255, 255, 0.9),
|
||||
inset 0 0 40px rgba(255, 255, 255, 0.2);
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
border: 1px solid rgba(226, 232, 240, 0.6);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.dark .glass-modal {
|
||||
background: rgba(15, 23, 42, 0.95);
|
||||
border-color: rgba(51, 65, 85, 0.7);
|
||||
box-shadow:
|
||||
0 50px 100px rgba(0, 0, 0, 0.5),
|
||||
inset 0 2px 0 rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(51, 65, 85, 0.6);
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
/* Glass Form Elements */
|
||||
/* ===== GLASS FORM ELEMENTE ===== */
|
||||
.glass-input {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
border: 1px solid rgba(226, 232, 240, 0.6);
|
||||
border-radius: 8px;
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(226, 232, 240, 0.5);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem 1rem;
|
||||
color: #0f172a;
|
||||
box-shadow:
|
||||
0 2px 8px rgba(0, 0, 0, 0.06),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.glass-input:focus {
|
||||
outline: none;
|
||||
border-color: rgba(0, 115, 206, 0.6);
|
||||
box-shadow:
|
||||
0 4px 12px rgba(0, 115, 206, 0.15),
|
||||
0 0 0 3px rgba(0, 115, 206, 0.1),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
||||
border-color: rgba(0, 115, 206, 0.5);
|
||||
box-shadow: 0 2px 8px rgba(0, 115, 206, 0.1);
|
||||
}
|
||||
|
||||
.dark .glass-input {
|
||||
background: rgba(30, 41, 59, 0.8);
|
||||
border-color: rgba(100, 116, 139, 0.6);
|
||||
border: 1px solid rgba(100, 116, 139, 0.5);
|
||||
color: #e2e8f0;
|
||||
box-shadow:
|
||||
0 2px 8px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* Glass Dropdown */
|
||||
.dark .glass-input:focus {
|
||||
border-color: rgba(59, 130, 246, 0.5);
|
||||
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.15);
|
||||
}
|
||||
|
||||
/* ===== GLASS DROPDOWN ===== */
|
||||
.glass-dropdown {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(24px) saturate(200%) brightness(120%);
|
||||
-webkit-backdrop-filter: blur(24px) saturate(200%) brightness(120%);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.dark .glass-dropdown {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.08);
|
||||
background: rgba(15, 23, 42, 0.9);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* Animation for glass elements */
|
||||
@keyframes glassFloat {
|
||||
0%, 100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
.glass-float {
|
||||
animation: glassFloat 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Glass overlay for backgrounds */
|
||||
/* ===== UTILITY CLASSES ===== */
|
||||
.glass-overlay {
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%);
|
||||
backdrop-filter: blur(40px) saturate(200%);
|
||||
-webkit-backdrop-filter: blur(40px) saturate(200%);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
.dark .glass-overlay {
|
||||
background: linear-gradient(135deg, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.1) 100%);
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* Responsive glass effects */
|
||||
/* ===== RESPONSIVE ANPASSUNGEN ===== */
|
||||
@media (max-width: 768px) {
|
||||
.glass-card {
|
||||
padding: 1rem;
|
||||
border-radius: 12px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.glass-modal {
|
||||
border-radius: 16px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.glass-base,
|
||||
.glass-strong,
|
||||
.glass-subtle {
|
||||
backdrop-filter: blur(6px);
|
||||
-webkit-backdrop-filter: blur(6px);
|
||||
}
|
||||
}
|
||||
|
||||
/* High contrast mode adjustments */
|
||||
/* ===== REDUZIERTE BEWEGUNG ===== */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.glass-card,
|
||||
.glass-btn,
|
||||
.glass-input {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== HOHER KONTRAST MODUS ===== */
|
||||
@media (prefers-contrast: high) {
|
||||
.glass-base,
|
||||
.glass-strong,
|
||||
.glass-card {
|
||||
border-width: 2px;
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
backdrop-filter: blur(4px);
|
||||
-webkit-backdrop-filter: blur(4px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reduced motion support */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.glass-interactive,
|
||||
.glass-btn,
|
||||
.glass-card {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.glass-interactive::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Glass Loading States */
|
||||
.glass-loading {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glass-loading::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.3),
|
||||
transparent);
|
||||
animation: glass-shimmer 2s infinite;
|
||||
}
|
||||
|
||||
.dark .glass-loading::after {
|
||||
background: linear-gradient(90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.1),
|
||||
transparent);
|
||||
}
|
||||
|
||||
@keyframes glass-shimmer {
|
||||
0% { left: -100%; }
|
||||
100% { left: 100%; }
|
||||
}
|
||||
|
||||
/* Premium Glow Effects for Light Mode */
|
||||
.glass-glow {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.glass-glow::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
left: -2px;
|
||||
right: -2px;
|
||||
bottom: -2px;
|
||||
background: linear-gradient(45deg,
|
||||
rgba(0, 115, 206, 0.1),
|
||||
rgba(0, 90, 159, 0.05),
|
||||
rgba(0, 115, 206, 0.1));
|
||||
border-radius: inherit;
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.glass-glow:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dark .glass-glow::after {
|
||||
background: linear-gradient(45deg,
|
||||
rgba(59, 130, 246, 0.2),
|
||||
rgba(29, 78, 216, 0.1),
|
||||
rgba(59, 130, 246, 0.2));
|
||||
/* ===== PERFORMANCE OPTIMIERUNGEN ===== */
|
||||
.glass-base,
|
||||
.glass-strong,
|
||||
.glass-subtle,
|
||||
.glass-card,
|
||||
.glass-btn,
|
||||
.glass-modal {
|
||||
will-change: transform;
|
||||
}
|
BIN
backend/static/css/glassmorphism.css.gz
Normal file
1
backend/static/css/glassmorphism.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.glass-base{background:rgba(255,255,255,0.85);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,0.3);box-shadow:0 8px 20px rgba(0,0,0,0.1);transition:all .2s ease}.glass-strong{background:rgba(255,255,255,0.9);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,0.4);box-shadow:0 12px 24px rgba(0,0,0,0.12)}.glass-subtle{background:rgba(255,255,255,0.8);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid rgba(255,255,255,0.2);box-shadow:0 4px 12px rgba(0,0,0,0.08)}.dark .glass-base{background:rgba(15,23,42,0.8);border:1px solid rgba(255,255,255,0.1);box-shadow:0 8px 20px rgba(0,0,0,0.3)}.dark .glass-strong{background:rgba(30,41,59,0.85);border:1px solid rgba(255,255,255,0.15);box-shadow:0 12px 24px rgba(0,0,0,0.4)}.dark .glass-subtle{background:rgba(15,23,42,0.7);border:1px solid rgba(255,255,255,0.08);box-shadow:0 4px 12px rgba(0,0,0,0.2)}.glass-card{background:rgba(255,255,255,0.9);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid rgba(229,231,235,0.5);border-radius:12px;box-shadow:0 8px 16px rgba(0,0,0,0.08);padding:1.5rem;transition:transform .2s ease,box-shadow .2s ease}.glass-card:hover{transform:translateY(-1px);box-shadow:0 12px 20px rgba(0,0,0,0.12)}.dark .glass-card{background:rgba(30,41,59,0.85);border:1px solid rgba(100,116,139,0.3);box-shadow:0 8px 16px rgba(0,0,0,0.25)}.dark .glass-card:hover{box-shadow:0 12px 20px rgba(0,0,0,0.35)}.glass-nav{background:rgba(255,255,255,0.9);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(226,232,240,0.4);border-bottom:1px solid rgba(203,213,225,0.5);box-shadow:0 4px 12px rgba(0,0,0,0.06)}.dark .glass-nav{background:rgba(15,23,42,0.85);border:1px solid rgba(51,65,85,0.5);box-shadow:0 4px 12px rgba(0,0,0,0.2)}.glass-btn{background:rgba(255,255,255,0.8);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid rgba(226,232,240,0.5);border-radius:8px;padding:.75rem 1.5rem;color:#0f172a;font-weight:600;box-shadow:0 2px 8px rgba(0,0,0,0.06);transition:transform .2s ease,background .2s ease}.glass-btn:hover{transform:translateY(-1px);background:rgba(255,255,255,0.9);box-shadow:0 4px 12px rgba(0,0,0,0.1)}.glass-btn-primary{background:rgba(0,115,206,0.9);color:white;box-shadow:0 2px 8px rgba(0,115,206,0.2)}.glass-btn-primary:hover{background:rgba(0,115,206,0.95);box-shadow:0 4px 12px rgba(0,115,206,0.3)}.dark .glass-btn{background:rgba(30,41,59,0.8);color:#e2e8f0;border:1px solid rgba(100,116,139,0.5);box-shadow:0 2px 8px rgba(0,0,0,0.15)}.dark .glass-btn:hover{background:rgba(30,41,59,0.9);box-shadow:0 4px 12px rgba(0,0,0,0.25)}.glass-modal{background:rgba(255,255,255,0.95);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid rgba(226,232,240,0.6);border-radius:16px;box-shadow:0 20px 40px rgba(0,0,0,0.12)}.dark .glass-modal{background:rgba(15,23,42,0.95);border:1px solid rgba(51,65,85,0.6);box-shadow:0 20px 40px rgba(0,0,0,0.4)}.glass-input{background:rgba(255,255,255,0.8);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid rgba(226,232,240,0.5);border-radius:6px;padding:.75rem 1rem;color:#0f172a;box-shadow:0 2px 4px rgba(0,0,0,0.04);transition:border-color .2s ease,box-shadow .2s ease}.glass-input:focus{outline:0;border-color:rgba(0,115,206,0.5);box-shadow:0 2px 8px rgba(0,115,206,0.1)}.dark .glass-input{background:rgba(30,41,59,0.8);border:1px solid rgba(100,116,139,0.5);color:#e2e8f0;box-shadow:0 2px 4px rgba(0,0,0,0.15)}.dark .glass-input:focus{border-color:rgba(59,130,246,0.5);box-shadow:0 2px 8px rgba(59,130,246,0.15)}.glass-dropdown{background:rgba(255,255,255,0.9);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,0.3);border-radius:8px;box-shadow:0 8px 20px rgba(0,0,0,0.15)}.dark .glass-dropdown{background:rgba(15,23,42,0.9);border:1px solid rgba(255,255,255,0.1);box-shadow:0 8px 20px rgba(0,0,0,0.3)}.glass-overlay{background:rgba(255,255,255,0.1);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px)}.dark .glass-overlay{background:rgba(0,0,0,0.2)}@media(max-width:768px){.glass-card{padding:1rem;border-radius:8px}.glass-modal{border-radius:12px}.glass-base,.glass-strong,.glass-subtle{backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}}@media(prefers-reduced-motion:reduce){.glass-card,.glass-btn,.glass-input{transition:none !important}}@media(prefers-contrast:high){.glass-base,.glass-strong,.glass-card{border-width:2px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}}.glass-base,.glass-strong,.glass-subtle,.glass-card,.glass-btn,.glass-modal{will-change:transform}
|
BIN
backend/static/css/glassmorphism.min.css.gz
Normal file
148
backend/static/css/icons-minimal.css
Normal file
@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Minimales Icon-Set für Kiosk-Modus
|
||||
* SVG-basierte Icons als CSS-Pseudo-Elemente für beste Performance
|
||||
* Ersetzt FontAwesome für deutlich kleinere Bundle-Größe
|
||||
*/
|
||||
|
||||
/* Icon-Basis-Klasse */
|
||||
.icon {
|
||||
display: inline-block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.icon-lg { width: 1.5rem; height: 1.5rem; }
|
||||
.icon-xl { width: 2rem; height: 2rem; }
|
||||
|
||||
/* ===== DRUCKER ICONS ===== */
|
||||
.icon-printer {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-print {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== STATUS ICONS ===== */
|
||||
.icon-check {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2310b981'%3E%3Cpath d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-warning {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23f59e0b'%3E%3Cpath d='M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-error {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ef4444'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-offline {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%236b7280'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== NAVIGATION ICONS ===== */
|
||||
.icon-home {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-settings {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-users {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M16 4c0-1.11.89-2 2-2s2 .89 2 2-.89 2-2 2-2-.89-2-2zm4 18v-6h2.5l-2.54-7.63A3.012 3.012 0 0 0 16.43 6c-.68 0-1.3.27-1.77.72L12 8.5l-2.66-1.78C8.87 6.27 8.25 6 7.57 6c-1.31 0-2.42.83-2.83 2L2.5 16H5v6h2v-6h2v6h2zm-6.5-10.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5S12 9.17 12 10s.67 1.5 1.5 1.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-dashboard {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== ACTION ICONS ===== */
|
||||
.icon-plus {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-edit {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-delete {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ef4444'%3E%3Cpath d='M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-refresh {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== POWER/CONNECTION ICONS ===== */
|
||||
.icon-power {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2310b981'%3E%3Cpath d='M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42C17.99 7.86 19 9.81 19 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.19 1.01-4.14 2.58-5.42L6.17 5.17C4.23 6.82 3 9.26 3 12c0 4.97 4.03 9 9 9s9-4.03 9-9c0-2.74-1.23-5.18-3.17-6.83z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-wifi {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2310b981'%3E%3Cpath d='M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.07 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== DOKUMENT ICONS ===== */
|
||||
.icon-file {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-queue {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== INFO ICONS ===== */
|
||||
.icon-info {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%233b82f6'%3E%3Cpath d='M12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2M13,17H11V11H13M13,9H11V7H13'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-time {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M16.2,16.2L11,13V7H12.5V12.2L17,14.7L16.2,16.2Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== ARROW ICONS ===== */
|
||||
.icon-arrow-right {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.icon-arrow-down {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23374151'%3E%3Cpath d='M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* ===== HOVER-STATES ===== */
|
||||
.btn:hover .icon,
|
||||
.nav-item:hover .icon {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* ===== DARK MODE ===== */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.icon-printer,
|
||||
.icon-print,
|
||||
.icon-home,
|
||||
.icon-settings,
|
||||
.icon-users,
|
||||
.icon-dashboard,
|
||||
.icon-plus,
|
||||
.icon-edit,
|
||||
.icon-refresh,
|
||||
.icon-file,
|
||||
.icon-queue,
|
||||
.icon-time,
|
||||
.icon-arrow-right,
|
||||
.icon-arrow-down {
|
||||
filter: brightness(2);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE ICON-SIZES ===== */
|
||||
@media (max-width: 768px) {
|
||||
.icon { width: 1.25rem; height: 1.25rem; }
|
||||
.icon-lg { width: 1.75rem; height: 1.75rem; }
|
||||
.icon-xl { width: 2.25rem; height: 2.25rem; }
|
||||
}
|
BIN
backend/static/css/input.css.gz
Normal file
1
backend/static/css/input.min.css
vendored
Normal file
BIN
backend/static/css/input.min.css.gz
Normal file
346
backend/static/css/kiosk-optimized.css
Normal file
@ -0,0 +1,346 @@
|
||||
/**
|
||||
* MYP Platform - Kiosk-Optimierte CSS
|
||||
* Maximale Performance für Offline-Kiosk-Umgebung
|
||||
* Keine Touch-Events, minimale Animationen, optimierte Rendering-Performance
|
||||
*/
|
||||
|
||||
/* ===== CRITICAL INLINE STYLES (sollten im HTML-Head stehen) ===== */
|
||||
:root {
|
||||
/* Reduzierte Farbvariablen für bessere Performance */
|
||||
--primary: #0073ce;
|
||||
--primary-dark: #005a9f;
|
||||
--bg: #fafbfc;
|
||||
--surface: #ffffff;
|
||||
--text: #111827;
|
||||
--text-muted: #6b7280;
|
||||
--border: #e5e7eb;
|
||||
--shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
/* CSS Containment für bessere Performance */
|
||||
* {
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
/* Basis-Reset ohne aufwendige Normalisierung */
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
line-height: 1.5;
|
||||
contain: layout style paint;
|
||||
/* Optimiert für Kiosk-Rendering */
|
||||
text-rendering: optimizeSpeed;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* ===== LAYOUT OPTIMIERUNGEN ===== */
|
||||
.header {
|
||||
background: var(--surface);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 1rem;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
color: var(--text-muted);
|
||||
transition: background-color 0.1s ease; /* Minimale Transition */
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ===== OPTIMIERTE KARTEN ===== */
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
box-shadow: var(--shadow);
|
||||
contain: layout style paint;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-1px); /* Minimaler Hover-Effekt */
|
||||
}
|
||||
|
||||
/* ===== BUTTONS OHNE KOMPLEXE ANIMATIONEN ===== */
|
||||
.btn {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.1s ease;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
/* ===== INPUTS OPTIMIERT ===== */
|
||||
.input {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 0.75rem;
|
||||
width: 100%;
|
||||
transition: border-color 0.1s ease;
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/* ===== TABELLEN OHNE KOMPLEXE EFFEKTE ===== */
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: var(--bg);
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.table td {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.table tr:hover {
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
/* ===== STATUS BADGES VEREINFACHT ===== */
|
||||
.status {
|
||||
display: inline-block;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.status-online { background: #d1fae5; color: #065f46; }
|
||||
.status-offline { background: #fee2e2; color: #991b1b; }
|
||||
.status-printing { background: #dbeafe; color: #1e40af; }
|
||||
|
||||
/* ===== GRID LAYOUTS OPTIMIERT ===== */
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
.grid-2 { grid-template-columns: repeat(2, 1fr); }
|
||||
.grid-3 { grid-template-columns: repeat(3, 1fr); }
|
||||
.grid-4 { grid-template-columns: repeat(4, 1fr); }
|
||||
|
||||
/* ===== UTILITIES MINIMAL ===== */
|
||||
.flex { display: flex; contain: layout; }
|
||||
.flex-col { flex-direction: column; }
|
||||
.items-center { align-items: center; }
|
||||
.justify-between { justify-content: space-between; }
|
||||
.gap-1 { gap: 0.25rem; }
|
||||
.gap-2 { gap: 0.5rem; }
|
||||
.gap-4 { gap: 1rem; }
|
||||
|
||||
.p-1 { padding: 0.25rem; }
|
||||
.p-2 { padding: 0.5rem; }
|
||||
.p-4 { padding: 1rem; }
|
||||
.p-6 { padding: 1.5rem; }
|
||||
|
||||
.m-1 { margin: 0.25rem; }
|
||||
.m-2 { margin: 0.5rem; }
|
||||
.m-4 { margin: 1rem; }
|
||||
|
||||
.mb-2 { margin-bottom: 0.5rem; }
|
||||
.mb-4 { margin-bottom: 1rem; }
|
||||
.mb-6 { margin-bottom: 1.5rem; }
|
||||
|
||||
.text-sm { font-size: 0.875rem; }
|
||||
.text-lg { font-size: 1.125rem; }
|
||||
.text-xl { font-size: 1.25rem; }
|
||||
.text-2xl { font-size: 1.5rem; }
|
||||
|
||||
.font-semibold { font-weight: 600; }
|
||||
.font-bold { font-weight: 700; }
|
||||
|
||||
.text-center { text-align: center; }
|
||||
.text-right { text-align: right; }
|
||||
|
||||
.w-full { width: 100%; }
|
||||
.h-full { height: 100%; }
|
||||
|
||||
.rounded { border-radius: 6px; }
|
||||
.rounded-lg { border-radius: 8px; }
|
||||
|
||||
.border { border: 1px solid var(--border); }
|
||||
.border-t { border-top: 1px solid var(--border); }
|
||||
.border-b { border-bottom: 1px solid var(--border); }
|
||||
|
||||
.bg-white { background: var(--surface); }
|
||||
.bg-gray-50 { background: var(--bg); }
|
||||
|
||||
.text-gray-600 { color: var(--text-muted); }
|
||||
.text-blue-600 { color: var(--primary); }
|
||||
|
||||
/* ===== DARK MODE MINIMAL ===== */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #1e293b;
|
||||
--surface: #334155;
|
||||
--text: #f8fafc;
|
||||
--text-muted: #94a3b8;
|
||||
--border: #475569;
|
||||
--shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE FÜR KIOSK-DISPLAYS ===== */
|
||||
@media (max-width: 1024px) {
|
||||
.grid-4 { grid-template-columns: repeat(2, 1fr); }
|
||||
.grid-3 { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.grid-4,
|
||||
.grid-3,
|
||||
.grid-2 { grid-template-columns: 1fr; }
|
||||
|
||||
.nav {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== PERFORMANCE OPTIMIERUNGEN ===== */
|
||||
/* Deaktivierung nicht benötigter Features für Kiosk */
|
||||
* {
|
||||
/* Keine Touch-Events */
|
||||
touch-action: none;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Nur notwendige Elemente selektierbar */
|
||||
input,
|
||||
textarea {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
/* Optimierte Scrolling-Performance */
|
||||
* {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* GPU-Acceleration nur wo nötig */
|
||||
.card:hover,
|
||||
.btn:hover {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* Minimale Animationen für bessere Performance */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
transition: none !important;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print-Optimierung (falls Kiosk drucken kann) */
|
||||
@media print {
|
||||
.nav,
|
||||
.btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: none;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== LAYOUT SHIFT PREVENTION ===== */
|
||||
img {
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Container für stabile Layouts */
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
/* ===== KIOSK-SPEZIFISCHE OPTIMIERUNGEN ===== */
|
||||
/* Vollbildmodus-Unterstützung */
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Fokus-Management für Keyboard-Navigation */
|
||||
:focus {
|
||||
outline: 2px solid var(--primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Kein Selection-Highlighting */
|
||||
::selection {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Optimierte Font-Loading */
|
||||
@font-face {
|
||||
font-family: 'system-ui';
|
||||
font-display: swap;
|
||||
}
|
@ -1,100 +1,27 @@
|
||||
/**
|
||||
* MYP Platform - Optimierungs-Animationen
|
||||
* Belohnende und motivierende Animationen für Auto-Optimierung
|
||||
* MYP Platform - Optimierte einfache Animationen
|
||||
* Reduzierte, performante Animationen für bessere User Experience
|
||||
*/
|
||||
|
||||
/* ===== MODAL FADE-IN ANIMATION ===== */
|
||||
/* ===== EINFACHE FADE-IN ANIMATION ===== */
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
backdrop-filter: blur(0px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fade-in 0.3s ease-out;
|
||||
animation: fade-in 0.2s ease-out;
|
||||
}
|
||||
|
||||
/* ===== BOUNCE-IN ANIMATION ===== */
|
||||
@keyframes bounce-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.3) translateY(-50px);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.05) translateY(0);
|
||||
}
|
||||
70% {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-bounce-in {
|
||||
animation: bounce-in 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
/* ===== PULSE-SCALE ANIMATION ===== */
|
||||
@keyframes pulse-scale {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-pulse-scale {
|
||||
animation: pulse-scale 3s infinite ease-in-out;
|
||||
}
|
||||
|
||||
/* ===== FLOATING ANIMATIONS ===== */
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0px) rotate(0deg);
|
||||
}
|
||||
33% {
|
||||
transform: translateY(-15px) rotate(5deg);
|
||||
}
|
||||
66% {
|
||||
transform: translateY(-5px) rotate(-3deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float-delay {
|
||||
0%, 100% {
|
||||
transform: translateY(0px) rotate(0deg);
|
||||
}
|
||||
33% {
|
||||
transform: translateY(-10px) rotate(-5deg);
|
||||
}
|
||||
66% {
|
||||
transform: translateY(-8px) rotate(3deg);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-float {
|
||||
animation: float 4s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.animate-float-delay {
|
||||
animation: float-delay 4s infinite ease-in-out;
|
||||
animation-delay: 1.5s;
|
||||
}
|
||||
|
||||
/* ===== SLIDE-UP ANIMATIONS ===== */
|
||||
/* ===== EINFACHE SLIDE-UP ANIMATION ===== */
|
||||
@keyframes slide-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
transform: translateY(15px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
@ -103,239 +30,105 @@
|
||||
}
|
||||
|
||||
.animate-slide-up {
|
||||
animation: slide-up 0.6s ease-out;
|
||||
animation: slide-up 0.3s ease-out;
|
||||
}
|
||||
|
||||
.animate-slide-up-delay {
|
||||
animation: slide-up 0.6s ease-out;
|
||||
animation-delay: 0.2s;
|
||||
animation: slide-up 0.3s ease-out;
|
||||
animation-delay: 0.1s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.animate-slide-up-delay-2 {
|
||||
animation: slide-up 0.6s ease-out;
|
||||
animation-delay: 0.4s;
|
||||
animation: slide-up 0.3s ease-out;
|
||||
animation-delay: 0.2s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.animate-slide-up-delay-3 {
|
||||
animation: slide-up 0.6s ease-out;
|
||||
animation-delay: 0.6s;
|
||||
animation-fill-mode: both;
|
||||
/* ===== EINFACHER HOVER-EFFEKT ===== */
|
||||
.animate-hover-lift {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
/* ===== COUNT-UP ANIMATION ===== */
|
||||
@keyframes count-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.5) rotate(-10deg);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.2) rotate(5deg);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) rotate(0deg);
|
||||
}
|
||||
.animate-hover-lift:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.animate-count-up {
|
||||
animation: count-up 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
animation-delay: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
/* ===== GLOW ANIMATION ===== */
|
||||
@keyframes glow {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 5px rgba(59, 130, 246, 0.5),
|
||||
0 0 10px rgba(59, 130, 246, 0.3),
|
||||
0 0 15px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 20px rgba(59, 130, 246, 0.8),
|
||||
0 0 30px rgba(59, 130, 246, 0.6),
|
||||
0 0 40px rgba(59, 130, 246, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-glow {
|
||||
animation: glow 3s infinite ease-in-out;
|
||||
}
|
||||
|
||||
/* ===== KONFETTI ANIMATION ===== */
|
||||
.confetti-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.confetti-piece {
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
top: -10px;
|
||||
border-radius: 2px;
|
||||
animation: confetti-fall linear infinite;
|
||||
}
|
||||
|
||||
@keyframes confetti-fall {
|
||||
/* ===== EINFACHE SUCCESS ANIMATION ===== */
|
||||
@keyframes simple-success {
|
||||
0% {
|
||||
transform: translateY(-100vh) rotate(0deg);
|
||||
opacity: 1;
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(120vh) rotate(720deg);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== ERFOLGS-BADGE SPEZIAL-ANIMATIONEN ===== */
|
||||
@keyframes star-twinkle {
|
||||
0%, 100% {
|
||||
opacity: 0.6;
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
}
|
||||
|
||||
.animate-success {
|
||||
animation: simple-success 0.3s ease-out;
|
||||
}
|
||||
|
||||
/* ===== EINFACHER PULSE FÜR LOADING ===== */
|
||||
@keyframes simple-pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
.badge-star {
|
||||
animation: star-twinkle 1.5s infinite ease-in-out;
|
||||
}
|
||||
|
||||
/* ===== LOADING SPINNER IMPROVEMENTS ===== */
|
||||
@keyframes spinner-glow {
|
||||
0% {
|
||||
filter: drop-shadow(0 0 5px rgba(59, 130, 246, 0.5));
|
||||
}
|
||||
50% {
|
||||
filter: drop-shadow(0 0 15px rgba(59, 130, 246, 0.8));
|
||||
}
|
||||
100% {
|
||||
filter: drop-shadow(0 0 5px rgba(59, 130, 246, 0.5));
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
#optimization-loader .animate-spin {
|
||||
animation: spin 1s linear infinite, spinner-glow 2s ease-in-out infinite;
|
||||
.animate-pulse-simple {
|
||||
animation: simple-pulse 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
/* ===== DARK MODE OPTIMIERUNGEN ===== */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.animate-glow {
|
||||
animation: glow-dark 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes glow-dark {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 5px rgba(96, 165, 250, 0.5),
|
||||
0 0 10px rgba(96, 165, 250, 0.3),
|
||||
0 0 15px rgba(96, 165, 250, 0.1);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 20px rgba(96, 165, 250, 0.8),
|
||||
0 0 30px rgba(96, 165, 250, 0.6),
|
||||
0 0 40px rgba(96, 165, 250, 0.4);
|
||||
}
|
||||
}
|
||||
/* ===== EINFACHER PROGRESS BAR ===== */
|
||||
.progress-fill {
|
||||
transition: width 1s ease-out;
|
||||
}
|
||||
|
||||
/* ===== PERFORMANCE OPTIMIERUNGEN ===== */
|
||||
.animate-bounce-in,
|
||||
.animate-fade-in,
|
||||
.animate-slide-up,
|
||||
.animate-slide-up-delay,
|
||||
.animate-slide-up-delay-2,
|
||||
.animate-slide-up-delay-3 {
|
||||
.animate-slide-up-delay-2 {
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
.animate-pulse-scale,
|
||||
.animate-float,
|
||||
.animate-float-delay {
|
||||
.animate-hover-lift {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.animate-glow {
|
||||
will-change: box-shadow;
|
||||
/* ===== UTILITY CLASSES ===== */
|
||||
.animate-smooth {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.animate-smooth-fast {
|
||||
transition: all 0.1s ease;
|
||||
}
|
||||
|
||||
/* ===== RESPONSIVE ANPASSUNGEN ===== */
|
||||
@media (max-width: 768px) {
|
||||
.confetti-piece {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
.animate-fade-in,
|
||||
.animate-slide-up,
|
||||
.animate-slide-up-delay,
|
||||
.animate-slide-up-delay-2 {
|
||||
animation-duration: 0.2s;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== REDUZIERTE BEWEGUNG UNTERSTÜTZUNG ===== */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
|
||||
#optimization-reward-modal .text-8xl {
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
#optimization-reward-modal .max-w-md {
|
||||
max-width: 90vw;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== BUTTON HOVER VERBESSERUNGEN ===== */
|
||||
button:hover .badge-star {
|
||||
animation-duration: 0.8s;
|
||||
}
|
||||
|
||||
/* ===== ZUSÄTZLICHE UTILITY CLASSES ===== */
|
||||
.animate-shake {
|
||||
animation: shake 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
|
||||
20%, 40%, 60%, 80% { transform: translateX(5px); }
|
||||
}
|
||||
|
||||
.animate-heartbeat {
|
||||
animation: heartbeat 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes heartbeat {
|
||||
0%, 100% { transform: scale(1); }
|
||||
14% { transform: scale(1.1); }
|
||||
28% { transform: scale(1); }
|
||||
42% { transform: scale(1.1); }
|
||||
70% { transform: scale(1); }
|
||||
}
|
||||
|
||||
/* ===== PROGRESS BAR ANIMATION ===== */
|
||||
.progress-fill {
|
||||
animation: progress-grow 2s ease-out;
|
||||
}
|
||||
|
||||
@keyframes progress-grow {
|
||||
from {
|
||||
width: 0%;
|
||||
}
|
||||
to {
|
||||
width: var(--progress-width, 100%);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== SUCCESS CHECKMARK ANIMATION ===== */
|
||||
.success-checkmark {
|
||||
animation: checkmark-draw 0.8s ease-out;
|
||||
}
|
||||
|
||||
@keyframes checkmark-draw {
|
||||
0% {
|
||||
stroke-dasharray: 0 100;
|
||||
}
|
||||
100% {
|
||||
stroke-dasharray: 100 0;
|
||||
.animate-hover-lift:hover {
|
||||
transform: none;
|
||||
}
|
||||
}
|
BIN
backend/static/css/optimization-animations.css.gz
Normal file
1
backend/static/css/optimization-animations.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
@keyframes fade-in{from{opacity:0}to{opacity:1}}.animate-fade-in{animation:fade-in .2s ease-out}@keyframes slide-up{from{opacity:0;transform:translateY(15px)}to{opacity:1;transform:translateY(0)}}.animate-slide-up{animation:slide-up .3s ease-out}.animate-slide-up-delay{animation:slide-up .3s ease-out;animation-delay:.1s;animation-fill-mode:both}.animate-slide-up-delay-2{animation:slide-up .3s ease-out;animation-delay:.2s;animation-fill-mode:both}.animate-hover-lift{transition:transform .2s ease}.animate-hover-lift:hover{transform:translateY(-2px)}@keyframes simple-success{0%{opacity:0;transform:scale(0.9)}100%{opacity:1;transform:scale(1)}}.animate-success{animation:simple-success .3s ease-out}@keyframes simple-pulse{0%,100%{opacity:1}50%{opacity:.6}}.animate-pulse-simple{animation:simple-pulse 2s infinite ease-in-out}.progress-fill{transition:width 1s ease-out}.animate-fade-in,.animate-slide-up,.animate-slide-up-delay,.animate-slide-up-delay-2{will-change:transform,opacity}.animate-hover-lift{will-change:transform}.animate-smooth{transition:all .2s ease}.animate-smooth-fast{transition:all .1s ease}@media(max-width:768px){.animate-fade-in,.animate-slide-up,.animate-slide-up-delay,.animate-slide-up-delay-2{animation-duration:.2s}}@media(prefers-reduced-motion:reduce){*{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}.animate-hover-lift:hover{transform:none}}
|
BIN
backend/static/css/optimization-animations.min.css.gz
Normal file
BIN
backend/static/css/output.css.gz
Normal file
1
backend/static/css/output.min.css
vendored
Normal file
BIN
backend/static/css/output.min.css.gz
Normal file
BIN
backend/static/css/printers.css.gz
Normal file
1
backend/static/css/printers.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.filter-btn{transition:all .2s ease-in-out}.filter-btn.active{background-color:white;box-shadow:0 1px 3px 0 rgba(0,0,0,0.1),0 1px 2px 0 rgba(0,0,0,0.06);color:#374151}.dark .filter-btn.active{background-color:#475569;color:#f1f5f9}.printer-card-online{background:linear-gradient(135deg,#f0fdf4 0,#fff 100%);border-color:#bbf7d0;box-shadow:0 1px 3px 0 rgba(34,197,94,0.1),0 1px 2px 0 rgba(34,197,94,0.06)}.dark .printer-card-online{background:linear-gradient(135deg,rgba(34,197,94,0.1) 0,#1e293b 100%);border-color:#166534;box-shadow:0 1px 3px 0 rgba(34,197,94,0.2),0 1px 2px 0 rgba(34,197,94,0.1)}.printer-card-online:hover{box-shadow:0 4px 6px -1px rgba(34,197,94,0.2),0 2px 4px -1px rgba(34,197,94,0.1)}.dark .printer-card-online:hover{box-shadow:0 4px 6px -1px rgba(34,197,94,0.3),0 2px 4px -1px rgba(34,197,94,0.2)}.online-indicator{animation:pulse-green 2s cubic-bezier(0.4,0,0.6,1) infinite}@keyframes pulse-green{0%,100%{opacity:1;box-shadow:0 0 0 0 rgba(34,197,94,0.7)}50%{opacity:.8;box-shadow:0 0 0 4px rgba(34,197,94,0)}}.status-count-change{animation:count-change .5s ease-in-out}@keyframes count-change{0%{transform:scale(1)}50%{transform:scale(1.1)}100%{transform:scale(1)}}.auto-refresh-active{background:linear-gradient(45deg,#10b981,#059669);animation:gradient-shift 3s ease-in-out infinite}@keyframes gradient-shift{0%,100%{background-position:0 50%}50%{background-position:100% 50%}}.printer-card{transition:all .3s cubic-bezier(0.4,0,0.2,1)}.printer-card:hover{transform:translateY(-2px)}.live-update-spinner{animation:spin 1s linear infinite}@keyframes spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}@media(max-width:640px){.filter-btn{padding:.375rem .75rem;font-size:.75rem}.status-overview{flex-direction:column;gap:.5rem}.printer-card{padding:1rem}}.dark .printer-card{background-color:#1e293b;border-color:#334155}.dark .printer-card:hover{background-color:#334155}.filter-btn:focus{outline:2px solid #3b82f6;outline-offset:2px}.printer-card:focus-within{outline:2px solid #3b82f6;outline-offset:2px}@media print{.filter-btn,.auto-refresh-btn,.printer-detail-btn,.delete-printer-btn{display:none}.printer-card{break-inside:avoid;box-shadow:none;border:1px solid #000}}@media(prefers-contrast:high){.printer-card-online{border:2px solid #059669}.online-indicator{border:1px solid #000}}@media(prefers-reduced-motion:reduce){.online-indicator,.auto-refresh-active,.live-update-spinner{animation:none}.printer-card{transition:none}.printer-card:hover{transform:none}}
|
BIN
backend/static/css/printers.min.css.gz
Normal file
@ -1,12 +1,11 @@
|
||||
/**
|
||||
* Mercedes-Benz MYP Platform - Erweiterte Professional Theme
|
||||
* Verbesserte Light/Dark Mode Implementierung mit optimierten Kontrasten
|
||||
* OPTIMIERT: Light Mode deutlich verbessert für bessere Lesbarkeit
|
||||
* Mercedes-Benz MYP Platform - Optimierte Professional Theme
|
||||
* Vereinfachte Light/Dark Mode Implementierung für bessere Performance
|
||||
*/
|
||||
|
||||
/* Globale CSS-Variablen für konsistente Theming */
|
||||
:root {
|
||||
/* Mercedes-Benz Markenfarben - Erweitert */
|
||||
/* Mercedes-Benz Markenfarben */
|
||||
--mb-primary: #0073ce;
|
||||
--mb-primary-dark: #005a9f;
|
||||
--mb-secondary: #64748b;
|
||||
@ -14,32 +13,22 @@
|
||||
--mb-black: #000000;
|
||||
--mb-silver: #c0c0c0;
|
||||
|
||||
/* Light Mode Farbpalette - DEUTLICH VERBESSERT für optimale Lesbarkeit */
|
||||
/* Light Mode Farbpalette */
|
||||
--light-bg-primary: #ffffff;
|
||||
--light-bg-secondary: #fafbfc;
|
||||
--light-bg-tertiary: #f3f5f7;
|
||||
--light-bg-accent: #fbfcfd;
|
||||
--light-surface: #ffffff;
|
||||
--light-surface-hover: #fafbfc;
|
||||
--light-surface-active: #f3f5f7;
|
||||
--light-text-primary: #111827; /* Deutlich verstärkter Kontrast */
|
||||
--light-text-secondary: #374151; /* Erhöhter Kontrast für bessere Lesbarkeit */
|
||||
--light-text-muted: #6b7280; /* Optimierter Muted-Text, immer noch lesbar */
|
||||
--light-text-primary: #111827;
|
||||
--light-text-secondary: #374151;
|
||||
--light-text-muted: #6b7280;
|
||||
--light-text-accent: #0073ce;
|
||||
--light-border: #e5e7eb; /* Sichtbarere aber elegante Borders */
|
||||
--light-border: #e5e7eb;
|
||||
--light-border-strong: #d1d5db;
|
||||
--light-border-accent: #0073ce15; /* Subtilerer Accent-Border */
|
||||
--light-shadow: rgba(0, 0, 0, 0.04); /* Sanftere, natürlichere Schatten */
|
||||
--light-shadow: rgba(0, 0, 0, 0.04);
|
||||
--light-shadow-strong: rgba(0, 0, 0, 0.08);
|
||||
--light-shadow-accent: rgba(0, 115, 206, 0.08);
|
||||
|
||||
/* Neue Light Mode Gradients - VERBESSERT für sanftere, harmonischere Optik */
|
||||
--light-gradient-primary: linear-gradient(135deg, #ffffff 0%, #fafbfc 25%, #f8fafc 75%, #f3f5f7 100%);
|
||||
--light-gradient-card: linear-gradient(135deg, #ffffff 0%, #fdfdfe 50%, #fafbfc 100%);
|
||||
--light-gradient-hero: linear-gradient(135deg, #fafbfc 0%, #f5f7f9 30%, #f0f3f6 70%, #f8fafc 100%);
|
||||
--light-gradient-accent: linear-gradient(135deg, #0073ce 0%, #005a9f 100%);
|
||||
|
||||
/* Dark Mode Farbpalette - UNVERÄNDERT (bereits perfekt) */
|
||||
/* Dark Mode Farbpalette */
|
||||
--dark-bg-primary: #0f172a;
|
||||
--dark-bg-secondary: #1e293b;
|
||||
--dark-bg-tertiary: #334155;
|
||||
@ -54,118 +43,81 @@
|
||||
--dark-shadow-strong: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
/* Professionelle Hero-Header Stile - VERBESSERT für Light Mode */
|
||||
/* Vereinfachte Hero-Header */
|
||||
.professional-hero {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 1.5rem;
|
||||
border-radius: 1rem;
|
||||
margin: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
background: var(--light-gradient-hero);
|
||||
background: var(--light-bg-secondary);
|
||||
border: 1px solid var(--light-border);
|
||||
box-shadow:
|
||||
0 8px 25px var(--light-shadow),
|
||||
0 4px 12px rgba(0, 115, 206, 0.03),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 12px var(--light-shadow);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.professional-hero:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow:
|
||||
0 12px 35px var(--light-shadow-strong),
|
||||
0 6px 16px var(--light-shadow-accent),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.95);
|
||||
box-shadow: 0 8px 20px var(--light-shadow-strong);
|
||||
}
|
||||
|
||||
.dark .professional-hero {
|
||||
background: linear-gradient(135deg, var(--dark-bg-primary) 0%, var(--dark-bg-secondary) 100%);
|
||||
background: var(--dark-bg-secondary);
|
||||
border: 1px solid var(--dark-border);
|
||||
box-shadow: 0 20px 40px var(--dark-shadow-strong);
|
||||
box-shadow: 0 8px 20px var(--dark-shadow);
|
||||
}
|
||||
|
||||
.professional-hero::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(45deg, transparent 30%, rgba(255, 255, 255, 0.06) 50%, transparent 70%);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.dark .professional-hero::before {
|
||||
background: linear-gradient(45deg, transparent 30%, rgba(255, 255, 255, 0.05) 50%, transparent 70%);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/* Hero Pattern Overlay - OPTIMIERT */
|
||||
.hero-pattern {
|
||||
background-image:
|
||||
radial-gradient(circle at 20% 20%, var(--light-border) 0.8px, transparent 0.8px),
|
||||
radial-gradient(circle at 80% 80%, var(--light-border) 0.8px, transparent 0.8px);
|
||||
background-size: 48px 48px;
|
||||
background-position: 0 0, 24px 24px;
|
||||
}
|
||||
|
||||
.dark .hero-pattern {
|
||||
background-image:
|
||||
radial-gradient(circle at 20% 20%, var(--dark-border) 1px, transparent 1px),
|
||||
radial-gradient(circle at 80% 80%, var(--dark-border) 1px, transparent 1px);
|
||||
}
|
||||
|
||||
/* Professionelle Container - VERBESSERT */
|
||||
/* Vereinfachte Container */
|
||||
.professional-container {
|
||||
background: var(--light-surface);
|
||||
border: 1px solid var(--light-border);
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 4px 20px var(--light-shadow);
|
||||
backdrop-filter: blur(16px);
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 2px 8px var(--light-shadow);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.dark .professional-container {
|
||||
background: var(--dark-surface);
|
||||
border: 1px solid var(--dark-border);
|
||||
box-shadow: 0 10px 30px var(--dark-shadow);
|
||||
box-shadow: 0 4px 12px var(--dark-shadow);
|
||||
}
|
||||
|
||||
.professional-container:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 30px var(--light-shadow-strong);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 16px var(--light-shadow-strong);
|
||||
}
|
||||
|
||||
.dark .professional-container:hover {
|
||||
box-shadow: 0 20px 40px var(--dark-shadow-strong);
|
||||
box-shadow: 0 8px 20px var(--dark-shadow-strong);
|
||||
}
|
||||
|
||||
/* Mercedes-Benz Glassmorphism Effekt - OPTIMIERT für Light Mode */
|
||||
/* Vereinfachte Glassmorphism-Effekte */
|
||||
.mb-glass {
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
backdrop-filter: blur(16px) saturate(180%);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(229, 231, 235, 0.4);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04);
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
transition: transform 0.2s ease, background 0.2s ease;
|
||||
}
|
||||
|
||||
.dark .mb-glass {
|
||||
background: rgba(15, 23, 42, 0.9);
|
||||
background: rgba(15, 23, 42, 0.85);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.mb-glass:hover {
|
||||
background: rgba(255, 255, 255, 0.97);
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.dark .mb-glass:hover {
|
||||
background: rgba(15, 23, 42, 0.95);
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
|
||||
background: rgba(15, 23, 42, 0.9);
|
||||
}
|
||||
|
||||
/* Professional Buttons - VERBESSERT */
|
||||
/* Vereinfachte Buttons */
|
||||
.btn-professional {
|
||||
background: var(--light-gradient-accent);
|
||||
background: linear-gradient(135deg, var(--mb-primary) 0%, var(--mb-primary-dark) 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
@ -174,73 +126,50 @@
|
||||
font-size: 0.875rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow:
|
||||
0 2px 8px rgba(0, 115, 206, 0.2),
|
||||
0 1px 3px rgba(0, 115, 206, 0.08);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-professional::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: left 0.5s ease;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 115, 206, 0.2);
|
||||
}
|
||||
|
||||
.btn-professional:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow:
|
||||
0 4px 15px rgba(0, 115, 206, 0.25),
|
||||
0 2px 8px rgba(0, 115, 206, 0.15);
|
||||
}
|
||||
|
||||
.btn-professional:hover::before {
|
||||
left: 100%;
|
||||
box-shadow: 0 4px 12px rgba(0, 115, 206, 0.3);
|
||||
}
|
||||
|
||||
.btn-professional:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Secondary Button Style - VERBESSERT */
|
||||
/* Secondary Button */
|
||||
.btn-secondary-professional {
|
||||
background: var(--light-surface);
|
||||
color: var(--light-text-primary);
|
||||
border: 1px solid var(--light-border-strong);
|
||||
border-radius: 0.75rem;
|
||||
padding: 0.75rem 1.75rem;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 8px var(--light-shadow);
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 1px 4px var(--light-shadow);
|
||||
}
|
||||
|
||||
.dark .btn-secondary-professional {
|
||||
background: var(--dark-surface);
|
||||
color: var(--dark-text-primary);
|
||||
border-color: var(--dark-border-strong);
|
||||
box-shadow: 0 4px 15px var(--dark-shadow);
|
||||
box-shadow: 0 2px 8px var(--dark-shadow);
|
||||
}
|
||||
|
||||
.btn-secondary-professional:hover {
|
||||
background: var(--light-surface-hover);
|
||||
border-color: var(--mb-primary);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 15px var(--light-shadow-strong);
|
||||
}
|
||||
|
||||
.dark .btn-secondary-professional:hover {
|
||||
background: var(--dark-surface-hover);
|
||||
box-shadow: 0 8px 25px var(--dark-shadow);
|
||||
}
|
||||
|
||||
/* Professional Input Fields - VERBESSERT */
|
||||
/* Vereinfachte Input Fields */
|
||||
.input-professional {
|
||||
background: var(--light-surface);
|
||||
border: 1px solid var(--light-border);
|
||||
@ -248,20 +177,20 @@
|
||||
padding: 0.75rem 1rem;
|
||||
color: var(--light-text-primary);
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 1px 6px var(--light-shadow);
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
box-shadow: 0 1px 4px var(--light-shadow);
|
||||
}
|
||||
|
||||
.dark .input-professional {
|
||||
background: var(--dark-surface);
|
||||
border-color: var(--dark-border);
|
||||
color: var(--dark-text-primary);
|
||||
box-shadow: 0 2px 8px var(--dark-shadow);
|
||||
box-shadow: 0 2px 4px var(--dark-shadow);
|
||||
}
|
||||
|
||||
.input-professional:focus {
|
||||
border-color: var(--mb-primary);
|
||||
box-shadow: 0 0 0 3px rgba(0, 115, 206, 0.06);
|
||||
box-shadow: 0 0 0 3px rgba(0, 115, 206, 0.1);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
@ -273,77 +202,51 @@
|
||||
color: var(--dark-text-muted);
|
||||
}
|
||||
|
||||
/* Professional Cards - VERBESSERT */
|
||||
/* Vereinfachte Cards */
|
||||
.card-professional {
|
||||
background: var(--light-gradient-card);
|
||||
background: var(--light-surface);
|
||||
border: 1px solid var(--light-border);
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.5rem;
|
||||
box-shadow:
|
||||
0 2px 12px var(--light-shadow),
|
||||
0 1px 4px rgba(0, 115, 206, 0.02),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-professional::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: var(--light-gradient-accent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
box-shadow: 0 2px 8px var(--light-shadow);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.card-professional:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 8px 25px var(--light-shadow-strong),
|
||||
0 4px 12px var(--light-shadow-accent),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.card-professional:hover::before {
|
||||
opacity: 1;
|
||||
box-shadow: 0 4px 16px var(--light-shadow-strong);
|
||||
}
|
||||
|
||||
.dark .card-professional {
|
||||
background: var(--dark-surface);
|
||||
border-color: var(--dark-border);
|
||||
box-shadow: 0 4px 15px var(--dark-shadow);
|
||||
box-shadow: 0 4px 12px var(--dark-shadow);
|
||||
}
|
||||
|
||||
/* Professional Statistics Cards - VERBESSERT */
|
||||
/* Vereinfachte Statistics Cards */
|
||||
.stat-card {
|
||||
background: var(--light-surface);
|
||||
border: 1px solid var(--light-border);
|
||||
border-radius: 0.75rem;
|
||||
padding: 1.5rem;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 12px var(--light-shadow);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
box-shadow: 0 2px 8px var(--light-shadow);
|
||||
}
|
||||
|
||||
.dark .stat-card {
|
||||
background: var(--dark-surface);
|
||||
border-color: var(--dark-border);
|
||||
box-shadow: 0 4px 15px var(--dark-shadow);
|
||||
box-shadow: 0 4px 12px var(--dark-shadow);
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-1px) scale(1.01);
|
||||
box-shadow: 0 6px 20px var(--light-shadow-strong);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 16px var(--light-shadow-strong);
|
||||
}
|
||||
|
||||
.dark .stat-card:hover {
|
||||
box-shadow: 0 8px 30px var(--dark-shadow-strong);
|
||||
box-shadow: 0 8px 20px var(--dark-shadow-strong);
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
@ -370,7 +273,7 @@
|
||||
color: var(--dark-text-muted);
|
||||
}
|
||||
|
||||
/* Professional Status Badges */
|
||||
/* Vereinfachte Status Badges */
|
||||
.status-professional {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@ -381,121 +284,61 @@
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
transition: all 0.2s ease;
|
||||
transition: transform 0.2s ease;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.status-professional:hover {
|
||||
transform: scale(1.05);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
/* Verbesserte Status Indicators */
|
||||
/* Status Indicators */
|
||||
.status-online {
|
||||
background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%);
|
||||
background: #ecfdf5;
|
||||
color: #065f46;
|
||||
border: 1px solid rgba(16, 185, 129, 0.2);
|
||||
box-shadow: 0 2px 8px rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
|
||||
.dark .status-online {
|
||||
background: linear-gradient(135deg, rgba(16, 185, 129, 0.15) 0%, rgba(16, 185, 129, 0.05) 100%);
|
||||
background: rgba(16, 185, 129, 0.15);
|
||||
color: #10b981;
|
||||
border-color: rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.status-offline {
|
||||
background: linear-gradient(135deg, #fef2f2 0%, #fecaca 100%);
|
||||
background: #fef2f2;
|
||||
color: #991b1b;
|
||||
border: 1px solid rgba(239, 68, 68, 0.2);
|
||||
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.1);
|
||||
}
|
||||
|
||||
.dark .status-offline {
|
||||
background: linear-gradient(135deg, rgba(239, 68, 68, 0.15) 0%, rgba(239, 68, 68, 0.05) 100%);
|
||||
background: rgba(239, 68, 68, 0.15);
|
||||
color: #ef4444;
|
||||
border-color: rgba(239, 68, 68, 0.3);
|
||||
}
|
||||
|
||||
.status-printing {
|
||||
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
|
||||
background: #eff6ff;
|
||||
color: #1d4ed8;
|
||||
border: 1px solid rgba(59, 130, 246, 0.2);
|
||||
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.dark .status-printing {
|
||||
background: linear-gradient(135deg, rgba(59, 130, 246, 0.15) 0%, rgba(59, 130, 246, 0.05) 100%);
|
||||
background: rgba(59, 130, 246, 0.15);
|
||||
color: #3b82f6;
|
||||
border-color: rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.status-maintenance {
|
||||
background: linear-gradient(135deg, #faf5ff 0%, #ede9fe 100%);
|
||||
color: #6b21a8;
|
||||
border: 1px solid rgba(139, 92, 246, 0.2);
|
||||
box-shadow: 0 2px 8px rgba(139, 92, 246, 0.1);
|
||||
}
|
||||
|
||||
.dark .status-maintenance {
|
||||
background: linear-gradient(135deg, rgba(139, 92, 246, 0.15) 0%, rgba(139, 92, 246, 0.05) 100%);
|
||||
color: #8b5cf6;
|
||||
border-color: rgba(139, 92, 246, 0.3);
|
||||
}
|
||||
|
||||
.status-pending {
|
||||
background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
|
||||
color: #92400e;
|
||||
border: 1px solid rgba(251, 191, 36, 0.2);
|
||||
box-shadow: 0 2px 8px rgba(251, 191, 36, 0.1);
|
||||
}
|
||||
|
||||
.dark .status-pending {
|
||||
background: linear-gradient(135deg, rgba(251, 191, 36, 0.15) 0%, rgba(251, 191, 36, 0.05) 100%);
|
||||
color: #fbbf24;
|
||||
border-color: rgba(251, 191, 36, 0.3);
|
||||
}
|
||||
|
||||
.status-approved {
|
||||
background: linear-gradient(135deg, #ecfdf5 0%, #a7f3d0 100%);
|
||||
color: #065f46;
|
||||
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||
box-shadow: 0 2px 8px rgba(16, 185, 129, 0.15);
|
||||
}
|
||||
|
||||
.dark .status-approved {
|
||||
background: linear-gradient(135deg, rgba(16, 185, 129, 0.2) 0%, rgba(16, 185, 129, 0.1) 100%);
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.status-denied {
|
||||
background: linear-gradient(135deg, #fef2f2 0%, #fca5a5 100%);
|
||||
color: #991b1b;
|
||||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||||
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.15);
|
||||
}
|
||||
|
||||
.dark .status-denied {
|
||||
background: linear-gradient(135deg, rgba(239, 68, 68, 0.2) 0%, rgba(239, 68, 68, 0.1) 100%);
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
/* Professional Typography */
|
||||
/* Vereinfachte Typography */
|
||||
.title-professional {
|
||||
background: linear-gradient(135deg, var(--light-text-primary) 0%, var(--light-text-accent) 50%, var(--light-text-secondary) 100%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
color: var(--light-text-primary);
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.1;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.dark .title-professional {
|
||||
background: linear-gradient(135deg, var(--dark-text-primary) 0%, var(--dark-text-secondary) 100%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
color: var(--dark-text-primary);
|
||||
}
|
||||
|
||||
.subtitle-professional {
|
||||
@ -512,21 +355,17 @@
|
||||
|
||||
/* Professional Navigation */
|
||||
.nav-professional {
|
||||
background: var(--light-gradient-card);
|
||||
background: var(--light-bg-secondary);
|
||||
border: 1px solid var(--light-border);
|
||||
border-radius: 1rem;
|
||||
border-radius: 0.75rem;
|
||||
padding: 0.5rem;
|
||||
box-shadow:
|
||||
0 4px 15px var(--light-shadow),
|
||||
0 2px 8px rgba(0, 115, 206, 0.05),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.6);
|
||||
backdrop-filter: blur(20px);
|
||||
box-shadow: 0 2px 8px var(--light-shadow);
|
||||
}
|
||||
|
||||
.dark .nav-professional {
|
||||
background: var(--dark-surface);
|
||||
background: var(--dark-bg-secondary);
|
||||
border-color: var(--dark-border);
|
||||
box-shadow: 0 4px 15px var(--dark-shadow);
|
||||
box-shadow: 0 4px 12px var(--dark-shadow);
|
||||
}
|
||||
|
||||
.nav-item-professional {
|
||||
@ -563,8 +402,8 @@
|
||||
background: linear-gradient(135deg, rgba(0, 115, 206, 0.1) 0%, rgba(0, 115, 206, 0.05) 100%);
|
||||
color: var(--mb-primary);
|
||||
font-weight: 600;
|
||||
border: 1px solid var(--light-border-accent);
|
||||
box-shadow: 0 2px 8px var(--light-shadow-accent);
|
||||
border: 1px solid var(--light-border-strong);
|
||||
box-shadow: 0 2px 8px var(--light-shadow);
|
||||
}
|
||||
|
||||
.dark .nav-item-professional.active {
|
||||
@ -575,23 +414,20 @@
|
||||
.table-professional {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: var(--light-gradient-card);
|
||||
border-radius: 1rem;
|
||||
background: var(--light-bg-secondary);
|
||||
border-radius: 0.75rem;
|
||||
overflow: hidden;
|
||||
box-shadow:
|
||||
0 4px 20px var(--light-shadow),
|
||||
0 2px 8px rgba(0, 115, 206, 0.05),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.6);
|
||||
box-shadow: 0 2px 8px var(--light-shadow);
|
||||
border: 1px solid var(--light-border);
|
||||
}
|
||||
|
||||
.dark .table-professional {
|
||||
background: var(--dark-surface);
|
||||
box-shadow: 0 4px 20px var(--dark-shadow);
|
||||
background: var(--dark-bg-secondary);
|
||||
box-shadow: 0 4px 12px var(--dark-shadow);
|
||||
}
|
||||
|
||||
.table-professional th {
|
||||
background: linear-gradient(135deg, var(--light-bg-secondary) 0%, var(--light-bg-tertiary) 100%);
|
||||
background: linear-gradient(135deg, var(--light-bg-tertiary) 0%, var(--light-bg-secondary) 100%);
|
||||
color: var(--light-text-primary);
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
@ -611,7 +447,7 @@
|
||||
}
|
||||
|
||||
.dark .table-professional th {
|
||||
background: var(--dark-bg-secondary);
|
||||
background: var(--dark-bg-tertiary);
|
||||
color: var(--dark-text-primary);
|
||||
border-bottom-color: var(--dark-border);
|
||||
}
|
||||
@ -723,7 +559,7 @@
|
||||
|
||||
/* Background Gradients für verschiedene Seiten */
|
||||
.bg-professional {
|
||||
background: var(--light-gradient-primary);
|
||||
background: var(--light-bg-secondary);
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
|
BIN
backend/static/css/professional-theme.css.gz
Normal file
1
backend/static/css/professional-theme.min.css
vendored
Normal file
BIN
backend/static/css/professional-theme.min.css.gz
Normal file
BIN
backend/static/css/tailwind.min.css.gz
Normal file
BIN
backend/static/fontawesome/css/all.css.gz
Normal file
BIN
backend/static/fontawesome/css/all.min.css.gz
Normal file
BIN
backend/static/fontawesome/css/brands.css.gz
Normal file
BIN
backend/static/fontawesome/css/brands.min.css.gz
Normal file
BIN
backend/static/fontawesome/css/fontawesome.css.gz
Normal file
BIN
backend/static/fontawesome/css/fontawesome.min.css.gz
Normal file
BIN
backend/static/fontawesome/css/regular.css.gz
Normal file
BIN
backend/static/fontawesome/css/regular.min.css.gz
Normal file
BIN
backend/static/fontawesome/css/solid.css.gz
Normal file
BIN
backend/static/fontawesome/css/solid.min.css.gz
Normal file
BIN
backend/static/fontawesome/css/svg-with-js.css.gz
Normal file
BIN
backend/static/fontawesome/css/svg-with-js.min.css.gz
Normal file
BIN
backend/static/fontawesome/css/v4-font-face.css.gz
Normal file
BIN
backend/static/fontawesome/css/v4-font-face.min.css.gz
Normal file
BIN
backend/static/fontawesome/css/v4-shims.css.gz
Normal file
BIN
backend/static/fontawesome/css/v4-shims.min.css.gz
Normal file
BIN
backend/static/fontawesome/css/v5-font-face.css.gz
Normal file
BIN
backend/static/fontawesome/css/v5-font-face.min.css.gz
Normal file
BIN
backend/static/fontawesome/js/all.js.gz
Normal file
BIN
backend/static/fontawesome/js/all.min.js.gz
Normal file
BIN
backend/static/fontawesome/js/brands.js.gz
Normal file
BIN
backend/static/fontawesome/js/brands.min.js.gz
Normal file
BIN
backend/static/fontawesome/js/conflict-detection.js.gz
Normal file
BIN
backend/static/fontawesome/js/conflict-detection.min.js.gz
Normal file
BIN
backend/static/fontawesome/js/fontawesome.js.gz
Normal file
BIN
backend/static/fontawesome/js/fontawesome.min.js.gz
Normal file
BIN
backend/static/fontawesome/js/regular.js.gz
Normal file
BIN
backend/static/fontawesome/js/regular.min.js.gz
Normal file
BIN
backend/static/fontawesome/js/solid.js.gz
Normal file
BIN
backend/static/fontawesome/js/solid.min.js.gz
Normal file
BIN
backend/static/fontawesome/js/v4-shims.js.gz
Normal file
BIN
backend/static/fontawesome/js/v4-shims.min.js.gz
Normal file
BIN
backend/static/fontawesome/metadata/icon-families.json.gz
Normal file
BIN
backend/static/fontawesome/package.json.gz
Normal file
BIN
backend/static/icons/apple-touch-icon.webp
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
backend/static/icons/favicon-16x16.webp
Normal file
After Width: | Height: | Size: 304 B |
BIN
backend/static/icons/favicon-32x32.webp
Normal file
After Width: | Height: | Size: 592 B |
BIN
backend/static/icons/icon-128x128.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
backend/static/icons/icon-144x144.webp
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
backend/static/icons/icon-152x152.webp
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
backend/static/icons/icon-192x192.webp
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
backend/static/icons/icon-384x384.webp
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
backend/static/icons/icon-512x512.webp
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
backend/static/icons/icon-72x72.webp
Normal file
After Width: | Height: | Size: 908 B |
BIN
backend/static/icons/icon-96x96.webp
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
backend/static/js/admin-guest-requests.js.gz
Normal file
223
backend/static/js/admin-guest-requests.min.js
vendored
Normal file
@ -0,0 +1,223 @@
|
||||
let currentRequests=[];let filteredRequests=[];let currentPage=0;let totalPages=0;let totalRequests=0;let refreshInterval=null;let csrfToken='';function detectApiBaseUrl(){return'';}
|
||||
const API_BASE_URL=detectApiBaseUrl();document.addEventListener('DOMContentLoaded',function(){csrfToken=document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')||'';initEventListeners();loadGuestRequests();startAutoRefresh();console.log('🎯 Admin Guest Requests Management geladen');});function initEventListeners(){const searchInput=document.getElementById('search-requests');if(searchInput){searchInput.addEventListener('input',debounce(handleSearch,300));}
|
||||
const statusFilter=document.getElementById('status-filter');if(statusFilter){statusFilter.addEventListener('change',handleFilterChange);}
|
||||
const sortOrder=document.getElementById('sort-order');if(sortOrder){sortOrder.addEventListener('change',handleSortChange);}
|
||||
const refreshBtn=document.getElementById('refresh-btn');if(refreshBtn){refreshBtn.addEventListener('click',()=>{loadGuestRequests();showNotification('🔄 Gastaufträge aktualisiert','info');});}
|
||||
const exportBtn=document.getElementById('export-btn');if(exportBtn){exportBtn.addEventListener('click',handleExport);}
|
||||
const bulkActionsBtn=document.getElementById('bulk-actions-btn');if(bulkActionsBtn){bulkActionsBtn.addEventListener('click',showBulkActionsModal);}
|
||||
const selectAllCheckbox=document.getElementById('select-all');if(selectAllCheckbox){selectAllCheckbox.addEventListener('change',handleSelectAll);}}
|
||||
async function loadGuestRequests(){try{showLoading(true);const url=`${API_BASE_URL}/api/admin/guest-requests`;const response=await fetch(url,{method:'GET',headers:{'Content-Type':'application/json','X-CSRFToken':csrfToken}});if(!response.ok){throw new Error(`HTTP ${response.status}:${response.statusText}`);}
|
||||
const data=await response.json();if(data.success){currentRequests=data.requests||[];totalRequests=data.total||0;updateStats(data.stats||{});applyFiltersAndSort();console.log(`✅ ${currentRequests.length}Gastaufträge geladen`);}else{throw new Error(data.message||'Fehler beim Laden der Gastaufträge');}}catch(error){console.error('Fehler beim Laden der Gastaufträge:',error);showNotification('❌ Fehler beim Laden der Gastaufträge: '+error.message,'error');showEmptyState();}finally{showLoading(false);}}
|
||||
function updateStats(stats){const elements={'pending-count':stats.pending||0,'approved-count':stats.approved||0,'rejected-count':stats.rejected||0,'total-count':stats.total||0};Object.entries(elements).forEach(([id,value])=>{const element=document.getElementById(id);if(element){animateCounter(element,value);}});}
|
||||
function animateCounter(element,targetValue){const currentValue=parseInt(element.textContent)||0;const difference=targetValue-currentValue;const steps=20;const stepValue=difference/steps;let step=0;const interval=setInterval(()=>{step++;const value=Math.round(currentValue+(stepValue*step));element.textContent=value;if(step>=steps){clearInterval(interval);element.textContent=targetValue;}},50);}
|
||||
function applyFiltersAndSort(){let requests=[...currentRequests];const statusFilter=document.getElementById('status-filter')?.value;if(statusFilter&&statusFilter!=='all'){requests=requests.filter(req=>req.status===statusFilter);}
|
||||
const searchTerm=document.getElementById('search-requests')?.value.toLowerCase();if(searchTerm){requests=requests.filter(req=>req.name?.toLowerCase().includes(searchTerm)||req.email?.toLowerCase().includes(searchTerm)||req.file_name?.toLowerCase().includes(searchTerm)||req.reason?.toLowerCase().includes(searchTerm));}
|
||||
const sortOrder=document.getElementById('sort-order')?.value;requests.sort((a,b)=>{switch(sortOrder){case'oldest':return new Date(a.created_at)-new Date(b.created_at);case'priority':return getPriorityValue(b)-getPriorityValue(a);case'newest':default:return new Date(b.created_at)-new Date(a.created_at);}});filteredRequests=requests;renderRequestsTable();}
|
||||
function getPriorityValue(request){const now=new Date();const created=new Date(request.created_at);const hoursOld=(now-created)/(1000*60*60);let priority=0;if(request.status==='pending')priority+=10;else if(request.status==='approved')priority+=5;if(hoursOld>24)priority+=5;else if(hoursOld>8)priority+=3;else if(hoursOld>2)priority+=1;return priority;}
|
||||
function renderRequestsTable(){const tableBody=document.getElementById('requests-table-body');const emptyState=document.getElementById('empty-state');if(!tableBody)return;if(filteredRequests.length===0){tableBody.innerHTML='';showEmptyState();return;}
|
||||
hideEmptyState();const requestsHtml=filteredRequests.map(request=>createRequestRow(request)).join('');tableBody.innerHTML=requestsHtml;addRowEventListeners();}
|
||||
function createRequestRow(request){const statusColor=getStatusColor(request.status);const priorityLevel=getPriorityLevel(request);const timeAgo=getTimeAgo(request.created_at);return`<tr class="hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors duration-200"data-request-id="${request.id}"><td class="px-6 py-4"><input type="checkbox"class="request-checkbox rounded border-slate-300 text-blue-600 focus:ring-blue-500"
|
||||
value="${request.id}"></td><td class="px-6 py-4"><div class="flex items-center"><div class="flex-shrink-0 h-10 w-10"><div class="h-10 w-10 rounded-full bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center text-white font-semibold">${request.name?request.name[0].toUpperCase():'G'}</div></div><div class="ml-4"><div class="text-sm font-medium text-slate-900 dark:text-white">${escapeHtml(request.name||'Unbekannt')}</div><div class="text-sm text-slate-500 dark:text-slate-400">${escapeHtml(request.email||'Keine E-Mail')}</div></div></div></td><td class="px-6 py-4"><div class="text-sm text-slate-900 dark:text-white font-medium">${escapeHtml(request.file_name||'Keine Datei')}</div><div class="text-sm text-slate-500 dark:text-slate-400">${request.duration_minutes?`${request.duration_minutes}Min.`:'Unbekannte Dauer'}
|
||||
${request.copies?`• ${request.copies}Kopien`:''}</div>${request.reason?`<div class="text-xs text-slate-400 dark:text-slate-500 mt-1 truncate max-w-xs">${escapeHtml(request.reason)}</div>`:''}</td><td class="px-6 py-4"><span class="inline-flex items-center px-2 py-1 text-xs font-semibold rounded-full ${statusColor}"><span class="w-2 h-2 mr-1 rounded-full ${getStatusDot(request.status)}"></span>${getStatusText(request.status)}</span></td><td class="px-6 py-4"><div class="text-sm text-slate-900 dark:text-white">${timeAgo}</div><div class="text-xs text-slate-500 dark:text-slate-400">${formatDateTime(request.created_at)}</div></td><td class="px-6 py-4"><div class="flex items-center">${getPriorityBadge(priorityLevel)}
|
||||
${request.is_urgent?'<span class="ml-2 text-red-500 text-xs">🔥 Dringend</span>':''}</div></td><td class="px-6 py-4"><div class="flex items-center space-x-2"><button onclick="showRequestDetail(${request.id})"
|
||||
class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300 transition-colors"
|
||||
title="Details anzeigen"><svg class="w-5 h-5"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button>${request.status==='pending'?`<button onclick="approveRequest(${request.id})"
|
||||
class="text-green-600 hover:text-green-900 dark:text-green-400 dark:hover:text-green-300 transition-colors"
|
||||
title="Genehmigen"><svg class="w-5 h-5"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg></button><button onclick="rejectRequest(${request.id})"
|
||||
class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 transition-colors"
|
||||
title="Ablehnen"><svg class="w-5 h-5"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg></button>`:''}<button onclick="deleteRequest(${request.id})"
|
||||
class="text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300 transition-colors"
|
||||
title="Löschen"><svg class="w-5 h-5"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg></button></div></td></tr>`;}
|
||||
function getStatusColor(status){const colors={'pending':'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300','approved':'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300','rejected':'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300','expired':'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-300'};return colors[status]||'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-300';}
|
||||
function getStatusDot(status){const dots={'pending':'bg-yellow-400 dark:bg-yellow-300','approved':'bg-green-400 dark:bg-green-300','rejected':'bg-red-400 dark:bg-red-300','expired':'bg-gray-400 dark:bg-gray-300'};return dots[status]||'bg-gray-400 dark:bg-gray-300';}
|
||||
function getStatusText(status){const texts={'pending':'Wartend','approved':'Genehmigt','rejected':'Abgelehnt','expired':'Abgelaufen'};return texts[status]||status;}
|
||||
function getPriorityLevel(request){const priority=getPriorityValue(request);if(priority>=15)return'high';if(priority>=8)return'medium';return'low';}
|
||||
function getPriorityBadge(level){const badges={'high':'<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300">🔴 Hoch</span>','medium':'<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300">🟡 Mittel</span>','low':'<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300">🟢 Niedrig</span>'};return badges[level]||badges['low'];}
|
||||
async function approveRequest(requestId){if(!confirm('Möchten Sie diesen Gastauftrag wirklich genehmigen?'))return;try{showLoading(true);const url=`${API_BASE_URL}/api/guest-requests/${requestId}/approve`;const response=await fetch(url,{method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':csrfToken},body:JSON.stringify({})});const data=await response.json();if(data.success){showNotification('✅ Gastauftrag erfolgreich genehmigt','success');loadGuestRequests();}else{throw new Error(data.message||'Fehler beim Genehmigen');}}catch(error){console.error('Fehler beim Genehmigen:',error);showNotification('❌ Fehler beim Genehmigen: '+error.message,'error');}finally{showLoading(false);}}
|
||||
async function rejectRequest(requestId){const reason=prompt('Grund für die Ablehnung:');if(!reason)return;try{showLoading(true);const url=`${API_BASE_URL}/api/guest-requests/${requestId}/reject`;const response=await fetch(url,{method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':csrfToken},body:JSON.stringify({reason})});const data=await response.json();if(data.success){showNotification('✅ Gastauftrag erfolgreich abgelehnt','success');loadGuestRequests();}else{throw new Error(data.message||'Fehler beim Ablehnen');}}catch(error){console.error('Fehler beim Ablehnen:',error);showNotification('❌ Fehler beim Ablehnen: '+error.message,'error');}finally{showLoading(false);}}
|
||||
async function deleteRequest(requestId){if(!confirm('Möchten Sie diesen Gastauftrag wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.'))return;try{showLoading(true);const url=`${API_BASE_URL}/api/guest-requests/${requestId}`;const response=await fetch(url,{method:'DELETE',headers:{'Content-Type':'application/json','X-CSRFToken':csrfToken}});const data=await response.json();if(data.success){showNotification('✅ Gastauftrag erfolgreich gelöscht','success');loadGuestRequests();}else{throw new Error(data.message||'Fehler beim Löschen');}}catch(error){console.error('Fehler beim Löschen:',error);showNotification('❌ Fehler beim Löschen: '+error.message,'error');}finally{showLoading(false);}}
|
||||
function showRequestDetail(requestId){const request=currentRequests.find(req=>req.id===requestId);if(!request)return;const modal=document.getElementById('detail-modal');const content=document.getElementById('modal-content');content.innerHTML=`<div class="p-6 border-b border-gray-200 dark:border-gray-700"><div class="flex justify-between items-center"><h3 class="text-xl font-bold text-gray-900 dark:text-white">Gastauftrag Details</h3><button onclick="closeDetailModal()"class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"><svg class="w-6 h-6"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M6 18L18 6M6 6l12 12"/></svg></button></div></div><div class="p-6"><div class="grid grid-cols-1 md:grid-cols-2 gap-6"><div class="space-y-4"><h4 class="text-lg font-semibold text-gray-900 dark:text-white">Antragsteller</h4><div class="bg-slate-50 dark:bg-slate-700 rounded-lg p-4"><p><strong>Name:</strong>${escapeHtml(request.name||'Unbekannt')}</p><p><strong>E-Mail:</strong>${escapeHtml(request.email||'Keine E-Mail')}</p><p><strong>Erstellt am:</strong>${formatDateTime(request.created_at)}</p></div></div><div class="space-y-4"><h4 class="text-lg font-semibold text-gray-900 dark:text-white">Auftrag Details</h4><div class="bg-slate-50 dark:bg-slate-700 rounded-lg p-4"><p><strong>Datei:</strong>${escapeHtml(request.file_name||'Keine Datei')}</p><p><strong>Dauer:</strong>${request.duration_minutes||'Unbekannt'}Minuten</p><p><strong>Kopien:</strong>${request.copies||1}</p><p><strong>Status:</strong>${getStatusText(request.status)}</p></div></div></div>${request.reason?`<div class="mt-6"><h4 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">Begründung</h4><div class="bg-slate-50 dark:bg-slate-700 rounded-lg p-4"><p class="text-gray-700 dark:text-gray-300">${escapeHtml(request.reason)}</p></div></div>`:''}<div class="mt-8 flex justify-end space-x-3">${request.status==='pending'?`<button onclick="approveRequest(${request.id}); closeDetailModal();"
|
||||
class="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors">Genehmigen</button><button onclick="rejectRequest(${request.id}); closeDetailModal();"
|
||||
class="px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors">Ablehnen</button>`:''}<button onclick="closeDetailModal()"
|
||||
class="px-4 py-2 bg-gray-300 dark:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-400 dark:hover:bg-gray-500 transition-colors">Schließen</button></div></div>`;modal.classList.remove('hidden');}
|
||||
function closeDetailModal(){const modal=document.getElementById('detail-modal');modal.classList.add('hidden');}
|
||||
function showBulkActionsModal(){const selectedIds=getSelectedRequestIds();if(selectedIds.length===0){showNotification('⚠️ Bitte wählen Sie mindestens einen Gastauftrag aus','warning');return;}
|
||||
const modal=document.getElementById('bulk-modal');modal.classList.remove('hidden');}
|
||||
function closeBulkModal(){const modal=document.getElementById('bulk-modal');modal.classList.add('hidden');}
|
||||
async function performBulkAction(action){const selectedIds=getSelectedRequestIds();if(selectedIds.length===0)return;const confirmMessages={'approve':`Möchten Sie ${selectedIds.length}Gastaufträge genehmigen?`,'reject':`Möchten Sie ${selectedIds.length}Gastaufträge ablehnen?`,'delete':`Möchten Sie ${selectedIds.length}Gastaufträge löschen?`};if(!confirm(confirmMessages[action]))return;try{showLoading(true);closeBulkModal();const promises=selectedIds.map(async(id)=>{const url=`${API_BASE_URL}/api/guest-requests/${id}/${action}`;const method=action==='delete'?'DELETE':'POST';return fetch(url,{method,headers:{'Content-Type':'application/json','X-CSRFToken':csrfToken}});});const results=await Promise.allSettled(promises);const successCount=results.filter(r=>r.status==='fulfilled'&&r.value.ok).length;showNotification(`✅ ${successCount}von ${selectedIds.length}Aktionen erfolgreich`,'success');loadGuestRequests();document.getElementById('select-all').checked=false;document.querySelectorAll('.request-checkbox').forEach(cb=>cb.checked=false);}catch(error){console.error('Fehler bei Bulk-Aktion:',error);showNotification('❌ Fehler bei der Bulk-Aktion: '+error.message,'error');}finally{showLoading(false);}}
|
||||
function getSelectedRequestIds(){const checkboxes=document.querySelectorAll('.request-checkbox:checked');return Array.from(checkboxes).map(cb=>parseInt(cb.value));}
|
||||
function handleSearch(){applyFiltersAndSort();}
|
||||
function handleFilterChange(){applyFiltersAndSort();}
|
||||
function handleSortChange(){applyFiltersAndSort();}
|
||||
function handleSelectAll(event){const checkboxes=document.querySelectorAll('.request-checkbox');checkboxes.forEach(checkbox=>{checkbox.checked=event.target.checked;});}
|
||||
function handleExport(){const selectedIds=getSelectedRequestIds();const exportData=selectedIds.length>0?filteredRequests.filter(req=>selectedIds.includes(req.id)):filteredRequests;if(exportData.length===0){showNotification('⚠️ Keine Daten zum Exportieren verfügbar','warning');return;}
|
||||
exportToCSV(exportData);}
|
||||
function exportToCSV(data){const headers=['ID','Name','E-Mail','Datei','Status','Erstellt','Dauer (Min)','Kopien','Begründung'];const rows=data.map(req=>[req.id,req.name||'',req.email||'',req.file_name||'',getStatusText(req.status),formatDateTime(req.created_at),req.duration_minutes||'',req.copies||'',req.reason||'']);const csvContent=[headers,...rows].map(row=>row.map(field=>`"${String(field).replace(/"/g,'""')}"`).join(','))
|
||||
.join('\n');
|
||||
|
||||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||
const link = document.createElement('a');
|
||||
|
||||
if (link.download !== undefined) {
|
||||
const url = URL.createObjectURL(blob);
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', `gastauftraege_${new Date().toISOString().split('T')[0]}.csv`);
|
||||
link.style.visibility = 'hidden';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
showNotification('📄 CSV-Export erfolgreich erstellt', 'success');
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-Refresh
|
||||
*/
|
||||
function startAutoRefresh() {
|
||||
// Refresh alle 30 Sekunden
|
||||
refreshInterval = setInterval(() => {
|
||||
loadGuestRequests();
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
function stopAutoRefresh() {
|
||||
if (refreshInterval) {
|
||||
clearInterval(refreshInterval);
|
||||
refreshInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility-Funktionen
|
||||
*/
|
||||
function addRowEventListeners() {
|
||||
// Falls notwendig, können hier zusätzliche Event Listener hinzugefügt werden
|
||||
}
|
||||
|
||||
function showLoading(show) {
|
||||
const loadingElement = document.getElementById('table-loading');
|
||||
const tableBody = document.getElementById('requests-table-body');
|
||||
|
||||
if (loadingElement) {
|
||||
loadingElement.classList.toggle('hidden', !show);
|
||||
}
|
||||
|
||||
if (show && tableBody) {
|
||||
tableBody.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
function showEmptyState() {
|
||||
const emptyState = document.getElementById('empty-state');
|
||||
if (emptyState) {
|
||||
emptyState.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function hideEmptyState() {
|
||||
const emptyState = document.getElementById('empty-state');
|
||||
if (emptyState) {
|
||||
emptyState.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function showNotification(message, type = 'info') {
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `fixed top-4 right-4 px-6 py-4 rounded-xl shadow-2xl z-50 transform transition-all duration-500 translate-x-full ${
|
||||
type === 'success' ? 'bg-green-500 text-white' :
|
||||
type === 'error' ? 'bg-red-500 text-white' :
|
||||
type === 'warning' ? 'bg-yellow-500 text-black' :
|
||||
'bg-blue-500 text-white'
|
||||
}`;
|
||||
|
||||
notification.innerHTML = `
|
||||
<div class="flex items-center space-x-3">
|
||||
<span class="text-lg">
|
||||
${type === 'success' ? '✅' :
|
||||
type === 'error' ? '❌' :
|
||||
type === 'warning' ? '⚠️' : 'ℹ️'}
|
||||
</span>
|
||||
<span class="font-medium">${message}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// Animation einblenden
|
||||
setTimeout(() => {
|
||||
notification.classList.remove('translate-x-full');
|
||||
}, 100);
|
||||
|
||||
// Nach 5 Sekunden entfernen
|
||||
setTimeout(() => {
|
||||
notification.classList.add('translate-x-full');
|
||||
setTimeout(() => notification.remove(), 5000);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
};
|
||||
return text ? String(text).replace(/[&<>"']/g, m => map[m]) : '';
|
||||
}
|
||||
|
||||
function formatDateTime(dateString) {
|
||||
if (!dateString) return 'Unbekannt';
|
||||
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleString('de-DE', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
}
|
||||
|
||||
function getTimeAgo(dateString) {
|
||||
if (!dateString) return 'Unbekannt';
|
||||
|
||||
const now = new Date();
|
||||
const date = new Date(dateString);
|
||||
const diffMs = now - date;
|
||||
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
||||
const diffDays = Math.floor(diffHours / 24);
|
||||
|
||||
if (diffDays > 0) {
|
||||
return `vor ${diffDays} Tag${diffDays === 1 ? '' : 'en'}`;
|
||||
} else if (diffHours > 0) {
|
||||
return `vor ${diffHours} Stunde${diffHours === 1 ? '' : 'n'}`;
|
||||
} else {
|
||||
const diffMinutes = Math.floor(diffMs / (1000 * 60));
|
||||
return `vor ${Math.max(1, diffMinutes)} Minute${diffMinutes === 1 ? '' : 'n'}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Globale Funktionen für onclick-Handler
|
||||
window.showRequestDetail = showRequestDetail;
|
||||
window.approveRequest = approveRequest;
|
||||
window.rejectRequest = rejectRequest;
|
||||
window.deleteRequest = deleteRequest;
|
||||
window.closeDetailModal = closeDetailModal;
|
||||
window.closeBulkModal = closeBulkModal;
|
||||
window.performBulkAction = performBulkAction;
|
||||
|
||||
console.log('📋 Admin Guest Requests JavaScript vollständig geladen'
|
BIN
backend/static/js/admin-guest-requests.min.js.gz
Normal file
BIN
backend/static/js/admin-panel.js.gz
Normal file
77
backend/static/js/admin-panel.min.js
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
document.addEventListener('DOMContentLoaded',function(){initializeAdminPanel();loadAdminStats();initializeActiveTab();initializeEventHandlers();setInterval(function(){if(document.visibilityState==='visible'){refreshActiveTabData();}},30000);});function initializeActiveTab(){const hash=window.location.hash.substring(1);if(hash){activateTab(hash);}else{const defaultTab=document.querySelector('.nav-tab');if(defaultTab){const tabName=defaultTab.getAttribute('data-tab');activateTab(tabName);}}}
|
||||
function activateTab(tabName){const tabs=document.querySelectorAll('.nav-tab');tabs.forEach(tab=>{if(tab.getAttribute('data-tab')===tabName){tab.classList.add('active');}else{tab.classList.remove('active');}});const tabPanes=document.querySelectorAll('.tab-pane');tabPanes.forEach(pane=>{if(pane.id===`${tabName}-tab`){pane.classList.add('active');pane.classList.remove('hidden');}else{pane.classList.remove('active');pane.classList.add('hidden');}});switch(tabName){case'users':loadUsers();break;case'printers':loadPrinters();break;case'scheduler':loadSchedulerStatus();break;case'system':loadSystemStats();break;case'logs':loadLogs();break;}
|
||||
window.location.hash=tabName;}
|
||||
function refreshActiveTabData(){const activeTab=document.querySelector('.nav-tab.active');if(!activeTab)return;const tabName=activeTab.getAttribute('data-tab');switch(tabName){case'users':loadUsers();break;case'printers':loadPrinters();break;case'scheduler':loadSchedulerStatus();break;case'system':loadSystemStats();break;case'logs':loadLogs();break;}
|
||||
loadAdminStats();}
|
||||
function initializeEventHandlers(){const addUserBtn=document.getElementById('add-user-btn');if(addUserBtn){addUserBtn.addEventListener('click',showAddUserModal);}
|
||||
const addPrinterBtn=document.getElementById('add-printer-btn');if(addPrinterBtn){addPrinterBtn.addEventListener('click',showAddPrinterModal);}
|
||||
const refreshLogsBtn=document.getElementById('refresh-logs-btn');if(refreshLogsBtn){refreshLogsBtn.addEventListener('click',refreshLogs);}
|
||||
const closeDeleteModalBtn=document.getElementById('close-delete-modal');if(closeDeleteModalBtn){closeDeleteModalBtn.addEventListener('click',closeDeleteModal);}
|
||||
const cancelDeleteBtn=document.getElementById('cancel-delete');if(cancelDeleteBtn){cancelDeleteBtn.addEventListener('click',closeDeleteModal);}
|
||||
const closeAddUserBtn=document.getElementById('close-add-user-modal');if(closeAddUserBtn){closeAddUserBtn.addEventListener('click',closeAddUserModal);}
|
||||
const cancelAddUserBtn=document.getElementById('cancel-add-user');if(cancelAddUserBtn){cancelAddUserBtn.addEventListener('click',closeAddUserModal);}
|
||||
const confirmAddUserBtn=document.getElementById('confirm-add-user');if(confirmAddUserBtn){confirmAddUserBtn.addEventListener('click',addUser);}
|
||||
const closeAddPrinterBtn=document.getElementById('close-add-printer-modal');if(closeAddPrinterBtn){closeAddPrinterBtn.addEventListener('click',closeAddPrinterModal);}
|
||||
const cancelAddPrinterBtn=document.getElementById('cancel-add-printer');if(cancelAddPrinterBtn){cancelAddPrinterBtn.addEventListener('click',closeAddPrinterModal);}
|
||||
const confirmAddPrinterBtn=document.getElementById('confirm-add-printer');if(confirmAddPrinterBtn){confirmAddPrinterBtn.addEventListener('click',addPrinter);}
|
||||
const startSchedulerBtn=document.getElementById('start-scheduler');if(startSchedulerBtn){startSchedulerBtn.addEventListener('click',startScheduler);}
|
||||
const stopSchedulerBtn=document.getElementById('stop-scheduler');if(stopSchedulerBtn){stopSchedulerBtn.addEventListener('click',stopScheduler);}
|
||||
const refreshButton=document.createElement('button');refreshButton.id='refresh-admin-btn';refreshButton.className='fixed bottom-8 right-8 p-3 bg-primary text-white rounded-full shadow-lg z-40';refreshButton.innerHTML=`<svg class="w-6 h-6"fill="none"viewBox="0 0 24 24"stroke="currentColor"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>`;refreshButton.title='Alle Daten aktualisieren';refreshButton.addEventListener('click',refreshAllData);if(!document.getElementById('refresh-admin-btn')){document.body.appendChild(refreshButton);}}
|
||||
function showAddUserModal(){const modal=document.getElementById('add-user-modal');if(modal){const form=document.getElementById('add-user-form');if(form){form.reset();}
|
||||
modal.classList.remove('hidden');modal.classList.add('show');modal.style.display='flex';}}
|
||||
function closeAddUserModal(){const modal=document.getElementById('add-user-modal');if(modal){modal.classList.remove('show');modal.classList.add('hidden');modal.style.display='none';}}
|
||||
async function addUser(){const form=document.getElementById('add-user-form');if(!form)return;const userData={email:document.getElementById('user-email').value,name:document.getElementById('user-name').value,password:document.getElementById('user-password').value,role:document.getElementById('user-role').value,status:document.getElementById('user-status').value};try{const response=await fetch('/api/users',{method:'POST',headers:{'Content-Type':'application/json','X-CSRF-Token':getCSRFToken()},body:JSON.stringify(userData)});if(!response.ok){throw new Error('Fehler beim Hinzufügen des Benutzers');}
|
||||
showNotification('Benutzer erfolgreich hinzugefügt','success');closeAddUserModal();loadUsers();}catch(error){console.error('Error adding user:',error);showNotification(error.message,'error');}}
|
||||
function showAddPrinterModal(){const modal=document.getElementById('add-printer-modal');if(modal){const form=document.getElementById('add-printer-form');if(form){form.reset();}
|
||||
modal.classList.remove('hidden');modal.classList.add('show');modal.style.display='flex';}}
|
||||
function closeAddPrinterModal(){const modal=document.getElementById('add-printer-modal');if(modal){modal.classList.remove('show');modal.classList.add('hidden');modal.style.display='none';}}
|
||||
async function addPrinter(){const form=document.getElementById('add-printer-form');if(!form)return;const printerData={name:document.getElementById('printer-name').value,model:document.getElementById('printer-model').value,ip_address:document.getElementById('printer-ip').value,location:document.getElementById('printer-location').value,status:document.getElementById('printer-status').value};try{const response=await fetch('/api/printers',{method:'POST',headers:{'Content-Type':'application/json','X-CSRF-Token':getCSRFToken()},body:JSON.stringify(printerData)});if(!response.ok){throw new Error('Fehler beim Hinzufügen des Druckers');}
|
||||
showNotification('Drucker erfolgreich hinzugefügt','success');closeAddPrinterModal();loadPrinters();}catch(error){console.error('Error adding printer:',error);showNotification(error.message,'error');}}
|
||||
function refreshLogs(){loadLogs();showNotification('Logs aktualisiert','info');}
|
||||
function closeDeleteModal(){const modal=document.getElementById('delete-confirm-modal');if(modal){modal.classList.remove('show');modal.style.display='none';}}
|
||||
function showDeleteConfirmation(id,type,name,callback){const modal=document.getElementById('delete-confirm-modal');const messageEl=document.getElementById('delete-message');const idField=document.getElementById('delete-id');const typeField=document.getElementById('delete-type');const confirmBtn=document.getElementById('confirm-delete');if(modal&&messageEl&&idField&&typeField&&confirmBtn){messageEl.textContent=`Möchten Sie ${type==='user'?'den Benutzer':'den Drucker'}"${name}"wirklich löschen?`;idField.value=id;typeField.value=type;confirmBtn.onclick=function(){if(typeof callback==='function'){callback(id,name);}
|
||||
closeDeleteModal();};modal.classList.add('show');modal.style.display='flex';}}
|
||||
function initializeAdminPanel(){const tabs=document.querySelectorAll('.nav-tab');tabs.forEach(tab=>{tab.addEventListener('click',function(){const tabName=this.getAttribute('data-tab');if(!tabName)return;activateTab(tabName);});});const logLevelFilter=document.getElementById('log-level-filter');if(logLevelFilter){logLevelFilter.addEventListener('change',function(){if(window.logsData){const level=this.value;if(level==='all'){window.filteredLogs=[...window.logsData];}else{window.filteredLogs=window.logsData.filter(log=>log.level.toLowerCase()===level);}
|
||||
renderLogs();}});}
|
||||
const confirmDeleteBtn=document.getElementById('confirm-delete');if(confirmDeleteBtn){confirmDeleteBtn.addEventListener('click',function(){const id=document.getElementById('delete-id').value;const type=document.getElementById('delete-type').value;if(type==='user'){deleteUser(id);}else if(type==='printer'){deletePrinter(id);}
|
||||
closeDeleteModal();});}}
|
||||
async function loadAdminStats(){try{const response=await fetch('/api/admin/stats');if(!response.ok){throw new Error('Fehler beim Laden der Admin-Statistiken');}
|
||||
const data=await response.json();const statsContainer=document.getElementById('admin-stats');if(!statsContainer){console.warn('Stats-Container nicht gefunden');return;}
|
||||
statsContainer.innerHTML=`<div class="stat-card"><div class="stat-icon"><svg class="w-10 h-10"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/></svg></div><div class="stat-title">Benutzer</div><div class="stat-value"id="admin-total-users-count">${data.total_users||0}</div><div class="stat-desc">Registrierte Benutzer</div></div><div class="stat-card"><div class="stat-icon"><svg class="w-10 h-10"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z"/></svg></div><div class="stat-title">Drucker</div><div class="stat-value"id="admin-total-printers-count">${data.total_printers||0}</div><div class="stat-desc">Verbundene Drucker</div></div><div class="stat-card"><div class="stat-icon"><svg class="w-10 h-10"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/></svg></div><div class="stat-title">Aktive Jobs</div><div class="stat-value"id="admin-active-jobs-count">${data.active_jobs||0}</div><div class="stat-desc">Laufende Druckaufträge</div></div><div class="stat-card"><div class="stat-icon"><svg class="w-10 h-10"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg></div><div class="stat-title">Erfolgsrate</div><div class="stat-value"id="admin-success-rate">${data.success_rate||'0%'}</div><div class="stat-desc">Erfolgreiche Druckaufträge</div></div>`;console.log('✅ Admin-Statistiken erfolgreich aktualisiert:',data);}catch(error){console.error('Error loading admin stats:',error);showNotification('Fehler beim Laden der Admin-Statistiken','error');}}
|
||||
async function loadUsers(){try{const response=await fetch('/api/users');if(!response.ok){throw new Error('Fehler beim Laden der Benutzer');}
|
||||
const data=await response.json();const userTableBody=document.querySelector('#users-tab table tbody');if(!userTableBody)return;let html='';if(data.users&&data.users.length>0){data.users.forEach(user=>{html+=`<tr class="border-b border-gray-200 dark:border-gray-700"><td class="px-4 py-3">${user.id}</td><td class="px-4 py-3">${user.name||'-'}</td><td class="px-4 py-3">${user.email}</td><td class="px-4 py-3"><span class="px-2 py-1 text-xs rounded-full ${user.active ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' : 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'}">${user.active?'Aktiv':'Inaktiv'}</span></td><td class="px-4 py-3">${user.role||'Benutzer'}</td><td class="px-4 py-3"><div class="flex justify-end space-x-2"><button onclick="editUser(${user.id})"class="p-1 text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-200"><svg class="w-5 h-5"fill="none"viewBox="0 0 24 24"stroke="currentColor"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg></button><button onclick="showDeleteConfirmation(${user.id}, 'user', '${user.name || user.email}', deleteUser)"class="p-1 text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-200"><svg class="w-5 h-5"fill="none"viewBox="0 0 24 24"stroke="currentColor"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg></button></div></td></tr>`;});}else{html=`<tr><td colspan="6"class="px-4 py-6 text-center text-gray-500 dark:text-gray-400">Keine Benutzer gefunden</td></tr>`;}
|
||||
userTableBody.innerHTML=html;}catch(error){console.error('Error loading users:',error);showNotification('Fehler beim Laden der Benutzer','error');}}
|
||||
async function loadPrinters(){try{const response=await fetch('/api/printers');if(!response.ok){throw new Error('Fehler beim Laden der Drucker');}
|
||||
const data=await response.json();const printerTableBody=document.querySelector('#printers-tab table tbody');if(!printerTableBody)return;let html='';if(data.printers&&data.printers.length>0){data.printers.forEach(printer=>{html+=`<tr class="border-b border-gray-200 dark:border-gray-700"><td class="px-4 py-3">${printer.id}</td><td class="px-4 py-3">${printer.name}</td><td class="px-4 py-3">${printer.model||'-'}</td><td class="px-4 py-3">${printer.ip_address||'-'}</td><td class="px-4 py-3">${printer.location||'-'}</td><td class="px-4 py-3"><span class="px-2 py-1 text-xs rounded-full ${printer.status === 'online' ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' : 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'}">${printer.status==='online'?'Online':'Offline'}</span></td><td class="px-4 py-3"><div class="flex justify-end space-x-2"><button onclick="editPrinter(${printer.id})"class="p-1 text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-200"><svg class="w-5 h-5"fill="none"viewBox="0 0 24 24"stroke="currentColor"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/></svg></button><button onclick="showDeleteConfirmation(${printer.id}, 'printer', '${printer.name}', deletePrinter)"class="p-1 text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-200"><svg class="w-5 h-5"fill="none"viewBox="0 0 24 24"stroke="currentColor"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg></button></div></td></tr>`;});}else{html=`<tr><td colspan="7"class="px-4 py-6 text-center text-gray-500 dark:text-gray-400">Keine Drucker gefunden</td></tr>`;}
|
||||
printerTableBody.innerHTML=html;}catch(error){console.error('Error loading printers:',error);showNotification('Fehler beim Laden der Drucker','error');}}
|
||||
async function loadSchedulerStatus(){try{const response=await fetch('/api/scheduler/status');if(!response.ok){throw new Error('Fehler beim Laden des Scheduler-Status');}
|
||||
const data=await response.json();const statusIndicator=document.getElementById('scheduler-status-indicator');const statusText=document.getElementById('scheduler-status-text');const startSchedulerBtn=document.getElementById('start-scheduler');const stopSchedulerBtn=document.getElementById('stop-scheduler');if(statusIndicator&&statusText){if(data.active){statusIndicator.classList.remove('bg-red-500');statusIndicator.classList.add('bg-green-500');statusText.textContent='Aktiv';if(startSchedulerBtn&&stopSchedulerBtn){startSchedulerBtn.disabled=true;stopSchedulerBtn.disabled=false;}}else{statusIndicator.classList.remove('bg-green-500');statusIndicator.classList.add('bg-red-500');statusText.textContent='Inaktiv';if(startSchedulerBtn&&stopSchedulerBtn){startSchedulerBtn.disabled=false;stopSchedulerBtn.disabled=true;}}}
|
||||
const schedulerDetails=document.getElementById('scheduler-details');if(schedulerDetails){schedulerDetails.innerHTML=`<div class="mb-4"><h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Letzte Ausführung</h4><p class="text-gray-800 dark:text-gray-100">${data.last_run?new Date(data.last_run).toLocaleString('de-DE'):'Nie'}</p></div><div class="mb-4"><h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Nächste Ausführung</h4><p class="text-gray-800 dark:text-gray-100">${data.next_run?new Date(data.next_run).toLocaleString('de-DE'):'Nicht geplant'}</p></div><div class="mb-4"><h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Ausführungsintervall</h4><p class="text-gray-800 dark:text-gray-100">${data.interval||'60'}Sekunden</p></div><div><h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Verarbeitete Jobs</h4><p class="text-gray-800 dark:text-gray-100">${data.processed_jobs||0}Jobs seit dem letzten Neustart</p></div>`;}
|
||||
const jobQueueContainer=document.getElementById('job-queue');if(jobQueueContainer&&data.pending_jobs){let queueHtml='';if(data.pending_jobs.length>0){queueHtml=`<div class="bg-white dark:bg-gray-800 shadow-md rounded-lg overflow-hidden"><table class="min-w-full"><thead class="bg-gray-50 dark:bg-gray-700"><tr><th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Job ID</th><th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Name</th><th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Geplant für</th><th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Status</th></tr></thead><tbody class="divide-y divide-gray-200 dark:divide-gray-700">`;data.pending_jobs.forEach(job=>{queueHtml+=`<tr><td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100">${job.id}</td><td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100">${job.name}</td><td class="px-4 py-3 text-sm text-gray-900 dark:text-gray-100">${new Date(job.start_time).toLocaleString('de-DE')}</td><td class="px-4 py-3 text-sm"><span class="px-2 py-1 text-xs rounded-full bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200">Warten</span></td></tr>`;});queueHtml+=`</tbody></table></div>`;}else{queueHtml=`<div class="bg-white dark:bg-gray-800 shadow-md rounded-lg p-6 text-center"><p class="text-gray-500 dark:text-gray-400">Keine ausstehenden Jobs in der Warteschlange</p></div>`;}
|
||||
jobQueueContainer.innerHTML=queueHtml;}}catch(error){console.error('Error loading scheduler status:',error);showNotification('Fehler beim Laden des Scheduler-Status','error');}}
|
||||
async function loadSystemStats(){try{const response=await fetch('/api/system/stats');if(!response.ok){throw new Error('Fehler beim Laden der Systemstatistiken');}
|
||||
const data=await response.json();const cpuUsageElement=document.getElementById('cpu-usage');if(cpuUsageElement){cpuUsageElement.style.width=`${data.cpu_usage||0}%`;cpuUsageElement.textContent=`${data.cpu_usage||0}%`;}
|
||||
const ramUsageElement=document.getElementById('ram-usage');if(ramUsageElement){ramUsageElement.style.width=`${data.ram_usage_percent||0}%`;ramUsageElement.textContent=`${data.ram_usage_percent||0}%`;}
|
||||
const diskUsageElement=document.getElementById('disk-usage');if(diskUsageElement){diskUsageElement.style.width=`${data.disk_usage_percent||0}%`;diskUsageElement.textContent=`${data.disk_usage_percent||0}%`;}
|
||||
const systemDetailsElement=document.getElementById('system-details');if(systemDetailsElement){systemDetailsElement.innerHTML=`<div class="mb-4"><h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">System</h4><p class="text-gray-800 dark:text-gray-100">${data.os_name||'Unbekannt'}${data.os_version||''}</p></div><div class="mb-4"><h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Laufzeit</h4><p class="text-gray-800 dark:text-gray-100">${data.uptime||'Unbekannt'}</p></div><div class="mb-4"><h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Python-Version</h4><p class="text-gray-800 dark:text-gray-100">${data.python_version||'Unbekannt'}</p></div><div><h4 class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">Server-Zeit</h4><p class="text-gray-800 dark:text-gray-100">${data.server_time?new Date(data.server_time).toLocaleString('de-DE'):'Unbekannt'}</p></div>`;}
|
||||
const systemEventsElement=document.getElementById('system-events');if(systemEventsElement&&data.recent_events){let eventsHtml='';if(data.recent_events.length>0){eventsHtml='<ul class="divide-y divide-gray-200 dark:divide-gray-700">';data.recent_events.forEach(event=>{let eventTypeClass='text-blue-600 dark:text-blue-400';switch(event.type.toLowerCase()){case'error':eventTypeClass='text-red-600 dark:text-red-400';break;case'warning':eventTypeClass='text-yellow-600 dark:text-yellow-400';break;case'success':eventTypeClass='text-green-600 dark:text-green-400';break;}
|
||||
eventsHtml+=`<li class="py-3"><div class="flex items-start"><span class="${eventTypeClass} mr-2"><svg class="h-5 w-5"fill="none"viewBox="0 0 24 24"stroke="currentColor"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg></span><div><p class="text-sm text-gray-800 dark:text-gray-200">${event.message}</p><p class="text-xs text-gray-500 dark:text-gray-400">${new Date(event.timestamp).toLocaleString('de-DE')}</p></div></div></li>`;});eventsHtml+='</ul>';}else{eventsHtml='<p class="text-center text-gray-500 dark:text-gray-400 py-4">Keine Ereignisse vorhanden</p>';}
|
||||
systemEventsElement.innerHTML=eventsHtml;}}catch(error){console.error('Error loading system stats:',error);showNotification('Fehler beim Laden der Systemstatistiken','error');}}
|
||||
async function loadLogs(){try{const response=await fetch('/api/logs');if(!response.ok){throw new Error('Fehler beim Laden der Logs');}
|
||||
const data=await response.json();window.logsData=data.logs||[];window.filteredLogs=[...window.logsData];renderLogs();}catch(error){console.error('Error loading logs:',error);showNotification('Fehler beim Laden der Logs','error');}}
|
||||
function renderLogs(){const logsContainer=document.getElementById('logs-container');if(!logsContainer)return;if(!window.filteredLogs||window.filteredLogs.length===0){logsContainer.innerHTML='<div class="p-8 text-center text-gray-500 dark:text-gray-400">Keine Logs gefunden</div>';return;}
|
||||
let html='';window.filteredLogs.forEach(log=>{let levelClass='';switch(log.level.toLowerCase()){case'error':levelClass='bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200';break;case'warning':levelClass='bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200';break;case'info':levelClass='bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200';break;case'debug':levelClass='bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200';break;default:levelClass='bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200';}
|
||||
html+=`<div class="log-entry border-l-4 border-gray-300 dark:border-gray-600 pl-4 py-3 mb-3"><div class="flex items-center justify-between mb-1"><div><span class="px-2 py-1 text-xs rounded-full ${levelClass} mr-2">${log.level}</span><span class="text-sm text-gray-500 dark:text-gray-400">${log.timestamp?new Date(log.timestamp).toLocaleString('de-DE'):''}</span></div><div class="text-sm text-gray-500 dark:text-gray-400">${log.source||'System'}</div></div><div class="text-sm text-gray-800 dark:text-gray-200">${log.message}</div>${log.details?`<div class="mt-1 text-xs text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-800 p-2 rounded overflow-auto"><pre>${log.details}</pre></div>`:''}</div>`;});logsContainer.innerHTML=html;}
|
||||
async function deleteUser(userId){try{const response=await fetch(`/api/users/${userId}`,{method:'DELETE',headers:{'X-CSRF-Token':getCSRFToken()}});if(!response.ok){throw new Error('Fehler beim Löschen des Benutzers');}
|
||||
showNotification('Benutzer erfolgreich gelöscht','success');loadUsers();}catch(error){console.error('Error deleting user:',error);showNotification(error.message,'error');}}
|
||||
async function deletePrinter(printerId){try{const response=await fetch(`/api/printers/${printerId}`,{method:'DELETE',headers:{'X-CSRF-Token':getCSRFToken()}});if(!response.ok){throw new Error('Fehler beim Löschen des Druckers');}
|
||||
showNotification('Drucker erfolgreich gelöscht','success');loadPrinters();}catch(error){console.error('Error deleting printer:',error);showNotification(error.message,'error');}}
|
||||
async function startScheduler(){try{const response=await fetch('/api/scheduler/start',{method:'POST',headers:{'X-CSRF-Token':getCSRFToken()}});if(!response.ok){throw new Error('Fehler beim Starten des Schedulers');}
|
||||
showNotification('Scheduler erfolgreich gestartet','success');loadSchedulerStatus();}catch(error){console.error('Error starting scheduler:',error);showNotification(error.message,'error');}}
|
||||
async function stopScheduler(){try{const response=await fetch('/api/scheduler/stop',{method:'POST',headers:{'X-CSRF-Token':getCSRFToken()}});if(!response.ok){throw new Error('Fehler beim Stoppen des Schedulers');}
|
||||
showNotification('Scheduler erfolgreich gestoppt','success');loadSchedulerStatus();}catch(error){console.error('Error stopping scheduler:',error);showNotification(error.message,'error');}}
|
||||
function getCSRFToken(){return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')||'';}
|
||||
function refreshAllData(){loadAdminStats();const activeTab=document.querySelector('.nav-tab.active');if(activeTab){const tabName=activeTab.getAttribute('data-tab');activateTab(tabName);}
|
||||
showNotification('Daten wurden aktualisiert','success');}
|
||||
function showNotification(message,type='info'){const notification=document.getElementById('notification');const messageEl=document.getElementById('notification-message');const iconEl=document.getElementById('notification-icon');if(!notification||!messageEl||!iconEl)return;messageEl.textContent=message;notification.classList.remove('notification-success','notification-error','notification-warning','notification-info');notification.classList.add(`notification-${type}`);let icon='';switch(type){case'success':icon='<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />';break;case'error':icon='<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />';break;case'warning':icon='<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />';break;case'info':default:icon='<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />';break;}
|
||||
iconEl.innerHTML=icon;notification.classList.remove('hidden');notification.classList.add('show');setTimeout(()=>{notification.classList.remove('show');setTimeout(()=>{notification.classList.add('hidden');},300);},5000);}
|
BIN
backend/static/js/admin-panel.min.js.gz
Normal file
BIN
backend/static/js/admin-unified.js.gz
Normal file
101
backend/static/js/admin-unified.min.js
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
class AdminDashboard{constructor(){this.csrfToken=null;this.updateInterval=null;this.eventListenersAttached=false;this.apiBaseUrl=this.detectApiBaseUrl();this.retryCount=0;this.maxRetries=3;this.isInitialized=false;this.init();}
|
||||
detectApiBaseUrl(){const currentHost=window.location.hostname;const currentPort=window.location.port;if(currentPort==='5000'){return'';}
|
||||
return`http:}
|
||||
init(){if(this.isInitialized){console.log('🔄 Admin Dashboard bereits initialisiert, überspringe...');return;}
|
||||
console.log('🚀 Initialisiere Mercedes-Benz MYP Admin Dashboard');this.csrfToken=this.extractCSRFToken();console.log('🔒 CSRF Token:',this.csrfToken?'verfügbar':'FEHLT!');this.attachEventListeners();this.startLiveUpdates();this.loadInitialData();this.isInitialized=true;console.log('✅ Admin Dashboard erfolgreich initialisiert');}
|
||||
extractCSRFToken(){const metaToken=document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');if(metaToken){console.log('🔒 CSRF Token aus meta Tag geladen');return metaToken;}
|
||||
const hiddenInput=document.querySelector('input[name="csrf_token"]')?.value;if(hiddenInput){console.log('🔒 CSRF Token aus hidden input geladen');return hiddenInput;}
|
||||
const cookieToken=document.cookie.split('; ').find(row=>row.startsWith('csrf_token='))?.split('=')[1];if(cookieToken){console.log('🔒 CSRF Token aus Cookie geladen');return cookieToken;}
|
||||
const flaskToken=document.querySelector('meta[name="csrf-token"]')?.content;if(flaskToken){console.log('🔒 CSRF Token aus Flask-WTF Meta geladen');return flaskToken;}
|
||||
console.error('❌ CSRF Token konnte nicht gefunden werden!');return null;}
|
||||
attachEventListeners(){if(this.eventListenersAttached){console.log('⚠️ Event-Listener bereits registriert, überspringe...');return;}
|
||||
this.attachSystemButtons();this.attachUserManagement();this.attachPrinterManagement();this.attachJobManagement();this.attachModalEvents();this.eventListenersAttached=true;console.log('📌 Event-Listener erfolgreich registriert');}
|
||||
attachSystemButtons(){this.addEventListenerSafe('#system-status-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.showSystemStatus();});this.addEventListenerSafe('#analytics-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.showAnalytics();});this.addEventListenerSafe('#clear-cache-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.clearSystemCache();});this.addEventListenerSafe('#optimize-db-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.optimizeDatabase();});this.addEventListenerSafe('#create-backup-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.createSystemBackup();});this.addEventListenerSafe('#force-init-printers-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.forceInitializePrinters();});}
|
||||
attachUserManagement(){this.addEventListenerSafe('#add-user-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.showUserModal();});document.addEventListener('click',(e)=>{if(e.target.closest('.edit-user-btn')){e.preventDefault();e.stopPropagation();const userId=e.target.closest('button').dataset.userId;this.editUser(userId);}
|
||||
if(e.target.closest('.delete-user-btn')){e.preventDefault();e.stopPropagation();const userId=e.target.closest('button').dataset.userId;const userName=e.target.closest('button').dataset.userName;this.deleteUser(userId,userName);}});}
|
||||
attachPrinterManagement(){this.addEventListenerSafe('#add-printer-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.showPrinterModal();});document.addEventListener('click',(e)=>{if(e.target.closest('.manage-printer-btn')){e.preventDefault();e.stopPropagation();const printerId=e.target.closest('button').dataset.printerId;this.managePrinter(printerId);}
|
||||
if(e.target.closest('.settings-printer-btn')){e.preventDefault();e.stopPropagation();const printerId=e.target.closest('button').dataset.printerId;this.showPrinterSettings(printerId);}
|
||||
if(e.target.closest('.toggle-printer-power-btn')){e.preventDefault();e.stopPropagation();const button=e.target.closest('button');const printerId=button.dataset.printerId;const printerName=button.dataset.printerName;this.togglePrinterPower(printerId,printerName,button);}});}
|
||||
attachJobManagement(){document.addEventListener('click',(e)=>{if(e.target.closest('.job-action-btn')){e.preventDefault();e.stopPropagation();const action=e.target.closest('button').dataset.action;const jobId=e.target.closest('button').dataset.jobId;this.handleJobAction(action,jobId);}});}
|
||||
attachModalEvents(){this.addEventListenerSafe('#fix-errors-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.fixErrors();});this.addEventListenerSafe('#dismiss-errors-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.dismissErrors();});this.addEventListenerSafe('#view-error-details-btn','click',(e)=>{e.preventDefault();e.stopPropagation();window.location.href='/admin-dashboard?tab=logs';});this.addEventListenerSafe('#refresh-logs-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.loadLogs();});this.addEventListenerSafe('#export-logs-btn','click',(e)=>{e.preventDefault();e.stopPropagation();this.exportLogs();});this.addEventListenerSafe('#log-level-filter','change',(e)=>{this.loadLogs();});}
|
||||
addEventListenerSafe(selector,event,handler){const element=document.querySelector(selector);if(element&&!element.dataset.listenerAttached){element.addEventListener(event,handler);element.dataset.listenerAttached='true';}}
|
||||
startLiveUpdates(){if(this.updateInterval){clearInterval(this.updateInterval);}
|
||||
this.updateInterval=setInterval(()=>{this.loadLiveStats();},30000);setInterval(()=>{this.updateLiveTime();},1000);setInterval(()=>{this.checkSystemHealth();},30000);console.log('🔄 Live-Updates gestartet');}
|
||||
async loadInitialData(){await this.loadLiveStats();await this.checkSystemHealth();setTimeout(()=>{this.testButtons();},1000);if(window.location.search.includes('tab=logs')||document.querySelector('.tabs [href*="logs"]')?.classList.contains('active')){await this.loadLogs();}
|
||||
const urlParams=new URLSearchParams(window.location.search);const activeTab=urlParams.get('tab');if(activeTab==='logs'){await this.loadLogs();}
|
||||
const logsContainer=document.getElementById('logs-container');if(logsContainer&&logsContainer.offsetParent!==null){await this.loadLogs();}}
|
||||
async loadLiveStats(){try{const url=`${this.apiBaseUrl}/api/stats`;const response=await fetch(url);if(!response.ok){throw new Error(`HTTP ${response.status}:${response.statusText}`);}
|
||||
const data=await response.json();this.updateStatsDisplay(data);this.retryCount=0;}catch(error){console.error('Fehler beim Laden der Live-Statistiken:',error);this.retryCount++;if(this.retryCount<=this.maxRetries){setTimeout(()=>this.loadLiveStats(),5000);}}}
|
||||
updateStatsDisplay(data){this.updateElement('live-users-count',data.total_users||0);this.updateElement('live-printers-count',data.total_printers||0);this.updateElement('live-printers-online',`${data.online_printers||0}online`);this.updateElement('live-jobs-active',data.active_jobs||0);this.updateElement('live-jobs-queued',`${data.queued_jobs||0}in Warteschlange`);this.updateElement('live-success-rate',`${data.success_rate||0}%`);this.updateProgressBar('users-progress',data.total_users,20);this.updateProgressBar('printers-progress',data.online_printers,data.total_printers);this.updateProgressBar('jobs-progress',data.active_jobs,10);this.updateProgressBar('success-progress',data.success_rate,100);console.log('📊 Live-Statistiken aktualisiert');}
|
||||
updateElement(elementId,value){const element=document.getElementById(elementId);if(element){element.textContent=value;}}
|
||||
updateProgressBar(progressId,currentValue,maxValue){const progressEl=document.getElementById(progressId);if(progressEl&¤tValue!==undefined&&maxValue>0){const percentage=Math.min(100,Math.max(0,(currentValue/maxValue)*100));progressEl.style.width=`${percentage}%`;}}
|
||||
updateLiveTime(){const timeElement=document.getElementById('live-time');if(timeElement){const now=new Date();timeElement.textContent=now.toLocaleTimeString('de-DE');}}
|
||||
async showSystemStatus(){console.log('🔧 System Status wird angezeigt');this.showNotification('System Status wird geladen...','info');}
|
||||
async showAnalytics(){console.log('📈 Analytics wird angezeigt');this.showNotification('Analytics werden geladen...','info');}
|
||||
async showMaintenance(){console.log('🛠️ Wartung wird angezeigt');const systemTab=document.querySelector('a[href*="tab=system"]');if(systemTab){systemTab.click();}}
|
||||
async clearSystemCache(){if(!confirm('🗑️ Möchten Sie wirklich den System-Cache leeren?'))return;try{const response=await fetch(`${this.apiBaseUrl}/api/admin/cache/clear`,{method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':this.csrfToken}});const data=await response.json();if(data.success){this.showNotification('✅ Cache erfolgreich geleert!','success');setTimeout(()=>window.location.reload(),2000);}else{this.showNotification('❌ Fehler beim Leeren des Cache','error');}}catch(error){this.showNotification('❌ Fehler beim Leeren des Cache','error');}}
|
||||
async optimizeDatabase(){if(!confirm('🔧 Möchten Sie die Datenbank optimieren?'))return;this.showNotification('🔄 Datenbank wird optimiert...','info');try{const response=await fetch(`${this.apiBaseUrl}/api/admin/database/optimize`,{method:'POST',headers:{'X-CSRFToken':this.csrfToken}});const data=await response.json();if(data.success){this.showNotification('✅ Datenbank erfolgreich optimiert!','success');}else{this.showNotification('❌ Fehler bei der Datenbank-Optimierung','error');}}catch(error){this.showNotification('❌ Fehler bei der Datenbank-Optimierung','error');}}
|
||||
async createSystemBackup(){if(!confirm('💾 Möchten Sie ein System-Backup erstellen?'))return;this.showNotification('🔄 Backup wird erstellt...','info');try{const response=await fetch(`${this.apiBaseUrl}/api/admin/backup/create`,{method:'POST',headers:{'X-CSRFToken':this.csrfToken}});const data=await response.json();if(data.success){this.showNotification('✅ Backup erfolgreich erstellt!','success');}else{this.showNotification('❌ Fehler beim Erstellen des Backups','error');}}catch(error){this.showNotification('❌ Fehler beim Erstellen des Backups','error');}}
|
||||
async forceInitializePrinters(){if(!confirm('🔄 Möchten Sie die Drucker-Initialisierung erzwingen?'))return;this.showNotification('🔄 Drucker werden initialisiert...','info');try{const response=await fetch(`${this.apiBaseUrl}/api/admin/printers/force-init`,{method:'POST',headers:{'X-CSRFToken':this.csrfToken}});const data=await response.json();if(data.success){this.showNotification('✅ Drucker erfolgreich initialisiert!','success');setTimeout(()=>window.location.reload(),2000);}else{this.showNotification('❌ Fehler bei der Drucker-Initialisierung','error');}}catch(error){this.showNotification('❌ Fehler bei der Drucker-Initialisierung','error');}}
|
||||
showUserModal(userId=null){const isEdit=userId!==null;const title=isEdit?'Benutzer bearbeiten':'Neuer Benutzer';const modalHtml=`<div id="user-modal"class="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4"><div class="bg-white dark:bg-slate-800 rounded-2xl p-8 max-w-md w-full shadow-2xl transform scale-100 transition-all duration-300"><div class="flex justify-between items-center mb-6"><h3 class="text-2xl font-bold text-slate-900 dark:text-white">${title}</h3><button onclick="this.closest('#user-modal').remove()"class="p-2 hover:bg-gray-100 dark:hover:bg-slate-700 rounded-lg transition-colors"><svg class="w-6 h-6 text-slate-500"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M6 18L18 6M6 6l12 12"/></svg></button></div><form id="user-form"class="space-y-4"><div><label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">E-Mail-Adresse*</label><input type="email"name="email"id="user-email"required
|
||||
class="w-full px-4 py-3 border border-slate-300 dark:border-slate-600 rounded-xl
|
||||
focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all
|
||||
dark:bg-slate-700 dark:text-white bg-slate-50"></div><div><label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Benutzername</label><input type="text"name="username"id="user-username"
|
||||
class="w-full px-4 py-3 border border-slate-300 dark:border-slate-600 rounded-xl
|
||||
focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all
|
||||
dark:bg-slate-700 dark:text-white bg-slate-50"></div><div><label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Name</label><input type="text"name="name"id="user-name"
|
||||
class="w-full px-4 py-3 border border-slate-300 dark:border-slate-600 rounded-xl
|
||||
focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all
|
||||
dark:bg-slate-700 dark:text-white bg-slate-50"></div><div><label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Passwort ${isEdit?'(leer lassen für keine Änderung)':'*'}</label><input type="password"name="password"id="user-password"${!isEdit?'required':''}
|
||||
class="w-full px-4 py-3 border border-slate-300 dark:border-slate-600 rounded-xl
|
||||
focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all
|
||||
dark:bg-slate-700 dark:text-white bg-slate-50"></div><div><label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Rolle</label><select name="role"id="user-role"
|
||||
class="w-full px-4 py-3 border border-slate-300 dark:border-slate-600 rounded-xl
|
||||
focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all
|
||||
dark:bg-slate-700 dark:text-white bg-slate-50"><option value="user">Benutzer</option><option value="admin">Administrator</option></select></div>${isEdit?`<div class="flex items-center space-x-2"><input type="checkbox"name="is_active"id="user-active"
|
||||
class="w-4 h-4 text-blue-600 bg-slate-100 border-slate-300 rounded focus:ring-blue-500"><label for="user-active"class="text-sm font-medium text-slate-700 dark:text-slate-300">Aktiv</label></div>`:''}<div class="flex justify-end space-x-3 mt-8 pt-6 border-t border-slate-200 dark:border-slate-600"><button type="button"onclick="this.closest('#user-modal').remove()"
|
||||
class="px-6 py-3 bg-slate-200 dark:bg-slate-600 text-slate-700 dark:text-slate-300
|
||||
rounded-xl hover:bg-slate-300 dark:hover:bg-slate-500 transition-colors font-medium">Abbrechen</button><button type="submit"id="user-submit-btn"
|
||||
class="px-6 py-3 bg-gradient-to-r from-blue-500 to-blue-600 text-white
|
||||
rounded-xl hover:from-blue-600 hover:to-blue-700 transition-all duration-300
|
||||
shadow-lg hover:shadow-xl font-medium">${isEdit?'Aktualisieren':'Erstellen'}</button></div></form></div></div>`;document.body.insertAdjacentHTML('beforeend',modalHtml);const form=document.getElementById('user-form');form.addEventListener('submit',(e)=>{e.preventDefault();e.stopPropagation();if(isEdit){this.updateUser(userId,new FormData(form));}else{this.createUser(new FormData(form));}});if(isEdit){this.loadUserData(userId);}
|
||||
setTimeout(()=>{document.getElementById('user-email').focus();},100);}
|
||||
async loadUserData(userId){try{const response=await fetch(`${this.apiBaseUrl}/api/admin/users/${userId}`);if(!response.ok){throw new Error(`HTTP ${response.status}:${response.statusText}`);}
|
||||
const data=await response.json();if(data.success){const user=data.user;document.getElementById('user-email').value=user.email||'';document.getElementById('user-username').value=user.username||'';document.getElementById('user-name').value=user.name||'';document.getElementById('user-role').value=user.is_admin?'admin':'user';const activeCheckbox=document.getElementById('user-active');if(activeCheckbox){activeCheckbox.checked=user.is_active!==false;}}else{this.showNotification('❌ Fehler beim Laden der Benutzerdaten','error');}}catch(error){console.error('Fehler beim Laden der Benutzerdaten:',error);this.showNotification('❌ Fehler beim Laden der Benutzerdaten','error');}}
|
||||
async createUser(formData){const submitBtn=document.getElementById('user-submit-btn');const originalText=submitBtn.innerHTML;try{submitBtn.innerHTML='<div class="animate-spin rounded-full h-5 w-5 border-b-2 border-white mx-auto"></div>';submitBtn.disabled=true;const userData={email:formData.get('email'),username:formData.get('username')||formData.get('email').split('@')[0],name:formData.get('name'),password:formData.get('password'),is_admin:formData.get('role')==='admin'};const response=await fetch(`${this.apiBaseUrl}/api/admin/users`,{method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':this.csrfToken},body:JSON.stringify(userData)});const data=await response.json();if(data.success){this.showNotification('✅ Benutzer erfolgreich erstellt!','success');document.getElementById('user-modal').remove();setTimeout(()=>{window.location.reload();},1000);}else{this.showNotification(`❌ Fehler:${data.error}`,'error');}}catch(error){console.error('Fehler beim Erstellen des Benutzers:',error);this.showNotification('❌ Fehler beim Erstellen des Benutzers','error');}finally{submitBtn.innerHTML=originalText;submitBtn.disabled=false;}}
|
||||
async updateUser(userId,formData){const submitBtn=document.getElementById('user-submit-btn');const originalText=submitBtn.innerHTML;try{submitBtn.innerHTML='<div class="animate-spin rounded-full h-5 w-5 border-b-2 border-white mx-auto"></div>';submitBtn.disabled=true;const userData={email:formData.get('email'),username:formData.get('username'),name:formData.get('name'),is_admin:formData.get('role')==='admin',is_active:formData.get('is_active')==='on'};const password=formData.get('password');if(password&&password.trim()){userData.password=password;}
|
||||
const response=await fetch(`${this.apiBaseUrl}/api/admin/users/${userId}`,{method:'PUT',headers:{'Content-Type':'application/json','X-CSRFToken':this.csrfToken},body:JSON.stringify(userData)});const data=await response.json();if(data.success){this.showNotification('✅ Benutzer erfolgreich aktualisiert!','success');document.getElementById('user-modal').remove();setTimeout(()=>{window.location.reload();},1000);}else{this.showNotification(`❌ Fehler:${data.error}`,'error');}}catch(error){console.error('Fehler beim Aktualisieren des Benutzers:',error);this.showNotification('❌ Fehler beim Aktualisieren des Benutzers','error');}finally{submitBtn.innerHTML=originalText;submitBtn.disabled=false;}}
|
||||
editUser(userId){console.log(`✏️ Benutzer ${userId}wird bearbeitet`);this.showUserModal(userId);}
|
||||
async deleteUser(userId,userName){if(!confirm(`🗑️ Möchten Sie den Benutzer"${userName}"wirklich löschen?\n\nDiese Aktion kann nicht rückgängig gemacht werden!`)){return;}
|
||||
try{this.showNotification(`🔄 Benutzer"${userName}"wird gelöscht...`,'info');const response=await fetch(`${this.apiBaseUrl}/api/admin/users/${userId}`,{method:'DELETE',headers:{'Content-Type':'application/json','X-CSRFToken':this.csrfToken}});const data=await response.json();if(data.success){this.showNotification(`✅ Benutzer"${userName}"erfolgreich gelöscht!`,'success');setTimeout(()=>{window.location.reload();},1000);}else{this.showNotification(`❌ Fehler beim Löschen:${data.error}`,'error');}}catch(error){console.error('Fehler beim Löschen des Benutzers:',error);this.showNotification('❌ Fehler beim Löschen des Benutzers','error');}}
|
||||
showPrinterModal(){console.log('🖨️ Drucker-Modal wird angezeigt');this.showNotification('Drucker-Funktionen werden geladen...','info');}
|
||||
managePrinter(printerId){console.log(`🔧 Drucker ${printerId}wird verwaltet`);this.showNotification(`Drucker ${printerId}wird verwaltet...`,'info');}
|
||||
showPrinterSettings(printerId){console.log(`⚙️ Drucker-Einstellungen ${printerId}werden angezeigt`);this.showNotification(`Drucker-Einstellungen werden geladen...`,'info');}
|
||||
async togglePrinterPower(printerId,printerName,button){console.log(`🔌 Smart-Plug Toggle für Drucker ${printerId}(${printerName})`);const confirmMessage=`Möchten Sie die Steckdose für"${printerName}"umschalten?\n\nDies schaltet den Drucker ein/aus.`;if(!confirm(confirmMessage))return;const originalContent=button.innerHTML;button.disabled=true;button.innerHTML=`<div class="animate-spin rounded-full h-4 w-4 border-2 border-white border-t-transparent"></div><span class="hidden lg:inline">Schaltet...</span>`;try{const response=await fetch(`${this.apiBaseUrl}/api/admin/printers/${printerId}/toggle`,{method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':this.csrfToken},body:JSON.stringify({reason:'Admin-Panel Smart-Plug Toggle'})});const data=await response.json();if(response.ok&&data.success){const action=data.action||'umgeschaltet';this.showNotification(`✅ Steckdose für"${printerName}"erfolgreich ${action}`,'success');button.classList.remove('from-orange-500','to-red-500');button.classList.add('from-green-500','to-green-600');setTimeout(()=>{button.classList.remove('from-green-500','to-green-600');button.classList.add('from-orange-500','to-red-500');},2000);setTimeout(()=>{this.loadLiveStats();},1000);}else{const errorMsg=data.error||'Unbekannter Fehler beim Schalten der Steckdose';this.showNotification(`❌ Fehler:${errorMsg}`,'error');console.error('Smart-Plug Toggle Fehler:',data);}}catch(error){console.error('Netzwerkfehler beim Smart-Plug Toggle:',error);this.showNotification(`❌ Netzwerkfehler beim Schalten der Steckdose für"${printerName}"`,'error');}finally{button.disabled=false;button.innerHTML=originalContent;}}
|
||||
handleJobAction(action,jobId){console.log(`📋 Job-Aktion"${action}"für Job ${jobId}`);this.showNotification(`Job-Aktion"${action}"wird ausgeführt...`,'info');}
|
||||
async checkSystemHealth(){try{const response=await fetch('/api/admin/system-health');const data=await response.json();if(data.success){this.updateHealthDisplay(data);this.updateErrorAlerts(data);}}catch(error){console.error('Fehler bei System-Health-Check:',error);}}
|
||||
updateHealthDisplay(data){const statusIndicator=document.getElementById('db-status-indicator');const statusText=document.getElementById('db-status-text');if(statusIndicator&&statusText){if(data.health_status==='critical'){statusIndicator.className='w-3 h-3 bg-red-500 rounded-full animate-pulse';statusText.textContent='Kritisch';statusText.className='text-sm font-medium text-red-600 dark:text-red-400';}else if(data.health_status==='warning'){statusIndicator.className='w-3 h-3 bg-yellow-500 rounded-full animate-pulse';statusText.textContent='Warnung';statusText.className='text-sm font-medium text-yellow-600 dark:text-yellow-400';}else{statusIndicator.className='w-3 h-3 bg-green-400 rounded-full animate-pulse';statusText.textContent='Gesund';statusText.className='text-sm font-medium text-green-600 dark:text-green-400';}}
|
||||
this.updateElement('last-migration',data.last_migration||'Unbekannt');this.updateElement('schema-integrity',data.schema_integrity||'Prüfung');this.updateElement('recent-errors-count',data.recent_errors_count||0);}
|
||||
updateErrorAlerts(data){const alertContainer=document.getElementById('critical-errors-alert');if(!alertContainer)return;const allErrors=[...(data.critical_errors||[]),...(data.warnings||[])];if(allErrors.length>0){alertContainer.classList.remove('hidden');}else{alertContainer.classList.add('hidden');}}
|
||||
async fixErrors(){if(!confirm('🔧 Möchten Sie die automatische Fehlerkorrektur durchführen?'))return;this.showNotification('🔄 Fehler werden automatisch behoben...','info');if(!this.csrfToken){console.error('❌ CSRF Token fehlt! Versuche Token neu zu laden...');this.csrfToken=this.extractCSRFToken();if(!this.csrfToken){this.showNotification('❌ Sicherheitsfehler: CSRF Token nicht verfügbar','error');return;}}
|
||||
console.log('🔧 Starte automatische Fehlerkorrektur...');console.log('🔒 CSRF Token für Request:',this.csrfToken.substring(0,10)+'...');try{const requestOptions={method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':this.csrfToken}};console.log('📡 Sende Request an:','/api/admin/fix-errors');console.log('📝 Request Headers:',requestOptions.headers);const response=await fetch('/api/admin/fix-errors',requestOptions);console.log('📡 Response Status:',response.status);console.log('📡 Response Headers:',Object.fromEntries(response.headers.entries()));if(!response.ok){const errorText=await response.text();console.error('❌ Response Error:',errorText);throw new Error(`HTTP ${response.status}:${response.statusText}-${errorText}`);}
|
||||
const data=await response.json();console.log('✅ Response Data:',data);if(data.success){this.showNotification('✅ Automatische Reparatur erfolgreich!','success');setTimeout(()=>this.checkSystemHealth(),2000);}else{this.showNotification(`❌ Automatische Reparatur fehlgeschlagen:${data.message||'Unbekannter Fehler'}`,'error');}}catch(error){console.error('❌ Fehler bei automatischer Reparatur:',error);this.showNotification(`❌ Fehler bei der automatischen Reparatur:${error.message}`,'error');}}
|
||||
dismissErrors(){const alertContainer=document.getElementById('critical-errors-alert');if(alertContainer){alertContainer.classList.add('hidden');}}
|
||||
showNotification(message,type='info'){let notification=document.getElementById('admin-notification');if(!notification){notification=document.createElement('div');notification.id='admin-notification';notification.className='fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg max-w-sm transition-all duration-300';document.body.appendChild(notification);}
|
||||
const colors={success:'bg-green-500 text-white',error:'bg-red-500 text-white',info:'bg-blue-500 text-white',warning:'bg-yellow-500 text-white'};notification.className=`fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg max-w-sm transition-all duration-300 ${colors[type]}`;notification.textContent=message;notification.style.transform='translateX(0)';setTimeout(()=>{if(notification){notification.style.transform='translateX(100%)';setTimeout(()=>{if(notification&¬ification.parentNode){notification.parentNode.removeChild(notification);}},300);}},3000);}
|
||||
async loadLogs(level=null){const logsContainer=document.getElementById('logs-container');if(!logsContainer)return;logsContainer.innerHTML=`<div class="flex justify-center items-center py-12"><div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 dark:border-blue-400"></div><span class="ml-3 text-slate-600 dark:text-slate-400">Logs werden geladen...</span></div>`;try{const filter=level||document.getElementById('log-level-filter')?.value||'all';const url=`${this.apiBaseUrl}/api/admin/logs?level=${filter}&limit=100`;const response=await fetch(url,{headers:{'X-CSRFToken':this.csrfToken}});if(!response.ok){throw new Error(`HTTP ${response.status}:${response.statusText}`);}
|
||||
const data=await response.json();this.displayLogs(data.logs||[]);console.log('📋 Logs erfolgreich geladen');}catch(error){console.error('Fehler beim Laden der Logs:',error);logsContainer.innerHTML=`<div class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl p-6 text-center"><svg class="w-12 h-12 text-red-500 mx-auto mb-4"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.464 0L4.35 16.5c-.77.833.192 2.5 1.732 2.5z"/></svg><h3 class="text-lg font-semibold text-red-800 dark:text-red-200 mb-2">Fehler beim Laden der Logs</h3><p class="text-red-600 dark:text-red-400 mb-4">${error.message}</p><button onclick="adminDashboard.loadLogs()"class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors">🔄 Erneut versuchen</button></div>`;}}
|
||||
displayLogs(logs){const logsContainer=document.getElementById('logs-container');if(!logsContainer)return;if(!logs||logs.length===0){logsContainer.innerHTML=`<div class="text-center py-12"><svg class="w-16 h-16 mx-auto text-slate-400 dark:text-slate-500 mb-4"fill="none"stroke="currentColor"viewBox="0 0 24 24"><path stroke-linecap="round"stroke-linejoin="round"stroke-width="2"d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg><h3 class="text-lg font-medium text-slate-900 dark:text-white mb-2">Keine Logs gefunden</h3><p class="text-slate-500 dark:text-slate-400">Es sind keine Logs für die ausgewählten Kriterien vorhanden.</p></div>`;return;}
|
||||
const logsHtml=logs.map(log=>{const levelColors={'error':'bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800 text-red-800 dark:text-red-200','warning':'bg-yellow-50 dark:bg-yellow-900/20 border-yellow-200 dark:border-yellow-800 text-yellow-800 dark:text-yellow-200','info':'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800 text-blue-800 dark:text-blue-200','debug':'bg-gray-50 dark:bg-gray-900/20 border-gray-200 dark:border-gray-800 text-gray-800 dark:text-gray-200','critical':'bg-red-100 dark:bg-red-900/40 border-red-300 dark:border-red-700 text-red-900 dark:text-red-100'};const levelIcons={'error':'❌','warning':'⚠️','info':'ℹ️','debug':'🔍','critical':'🚨'};const levelClass=levelColors[log.level]||levelColors['info'];const levelIcon=levelIcons[log.level]||'ℹ️';return`<div class="border rounded-xl p-4 transition-all duration-200 hover:shadow-md ${levelClass}"><div class="flex items-start justify-between mb-2"><div class="flex items-center space-x-2"><span class="text-lg">${levelIcon}</span><span class="font-semibold text-sm uppercase tracking-wide">${log.level}</span><span class="text-xs opacity-75">${log.component||'System'}</span></div><div class="text-xs opacity-75">${this.formatLogTimestamp(log.timestamp)}</div></div><div class="mb-2"><p class="font-medium">${this.escapeHtml(log.message)}</p>${log.details?`<p class="text-sm opacity-75 mt-1">${this.escapeHtml(log.details)}</p>`:''}</div>${log.user?`<div class="text-xs opacity-75"><span class="font-medium">Benutzer:</span>${this.escapeHtml(log.user)}</div>`:''}
|
||||
${log.ip_address?`<div class="text-xs opacity-75"><span class="font-medium">IP:</span>${this.escapeHtml(log.ip_address)}</div>`:''}
|
||||
${log.request_id?`<div class="text-xs opacity-75 mt-2 font-mono"><span class="font-medium">Request-ID:</span>${this.escapeHtml(log.request_id)}</div>`:''}</div>`;}).join('');logsContainer.innerHTML=logsHtml;}
|
||||
formatLogTimestamp(timestamp){if(!timestamp)return'Unbekannt';try{const date=new Date(timestamp);return date.toLocaleString('de-DE',{year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',second:'2-digit'});}catch(error){return timestamp;}}
|
||||
escapeHtml(text){if(!text)return'';const div=document.createElement('div');div.textContent=text;return div.innerHTML;}
|
||||
async exportLogs(){try{this.showNotification('📥 Logs werden exportiert...','info');const filter=document.getElementById('log-level-filter')?.value||'all';const url=`${this.apiBaseUrl}/api/admin/logs/export?level=${filter}`;const response=await fetch(url,{headers:{'X-CSRFToken':this.csrfToken}});if(!response.ok){throw new Error(`HTTP ${response.status}:${response.statusText}`);}
|
||||
const blob=await response.blob();const downloadUrl=window.URL.createObjectURL(blob);const a=document.createElement('a');a.href=downloadUrl;a.download=`system-logs-${new Date().toISOString().split('T')[0]}.csv`;document.body.appendChild(a);a.click();document.body.removeChild(a);window.URL.revokeObjectURL(downloadUrl);this.showNotification('✅ Logs erfolgreich exportiert!','success');}catch(error){console.error('Fehler beim Exportieren der Logs:',error);this.showNotification('❌ Fehler beim Exportieren der Logs: '+error.message,'error');}}
|
||||
testButtons(){console.log('🧪 Teste Button-Funktionalität...');const fixBtn=document.querySelector('#fix-errors-btn');if(fixBtn){console.log('✅ Fix-Errors Button gefunden:',fixBtn);console.log('🔗 Event-Listener-Status:',fixBtn.dataset.listenerAttached);fixBtn.addEventListener('click',(e)=>{console.log('🖱️ Fix-Errors Button wurde geklickt (manueller Listener)');e.preventDefault();e.stopPropagation();this.testFixErrors();});}else{console.error('❌ Fix-Errors Button NICHT gefunden!');}
|
||||
const viewBtn=document.querySelector('#view-error-details-btn');if(viewBtn){console.log('✅ View-Details Button gefunden:',viewBtn);console.log('🔗 Event-Listener-Status:',viewBtn.dataset.listenerAttached);viewBtn.addEventListener('click',(e)=>{console.log('🖱️ View-Details Button wurde geklickt (manueller Listener)');e.preventDefault();e.stopPropagation();console.log('🔄 Weiterleitung zu Logs-Tab...');window.location.href='/admin-dashboard?tab=logs';});}else{console.error('❌ View-Details Button NICHT gefunden!');}}
|
||||
async testFixErrors(){console.log('🧪 TEST: Fix-Errors wird ausgeführt...');console.log('🔒 Aktueller CSRF Token:',this.csrfToken);if(!this.csrfToken){console.error('❌ CSRF Token fehlt - versuche neu zu laden...');this.csrfToken=this.extractCSRFToken();console.log('🔒 Neu geladener Token:',this.csrfToken);}
|
||||
this.showNotification('🧪 TEST: Starte automatische Fehlerkorrektur...','info');try{const requestOptions={method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':this.csrfToken,'X-Requested-With':'XMLHttpRequest'}};console.log('📡 TEST Request an:','/api/admin/fix-errors');console.log('📝 TEST Headers:',requestOptions.headers);const response=await fetch('/api/admin/fix-errors',requestOptions);console.log('📡 TEST Response Status:',response.status);console.log('📡 TEST Response Headers:',Object.fromEntries(response.headers.entries()));if(!response.ok){const errorText=await response.text();console.error('❌ TEST Response Error:',errorText);this.showNotification(`❌ TEST Fehler:${response.status}-${errorText}`,'error');return;}
|
||||
const data=await response.json();console.log('✅ TEST Response Data:',data);if(data.success){this.showNotification('✅ TEST: Automatische Reparatur erfolgreich!','success');}else{this.showNotification(`❌ TEST:Reparatur fehlgeschlagen-${data.message}`,'error');}}catch(error){console.error('❌ TEST Fehler:',error);this.showNotification(`❌ TEST Netzwerk-Fehler:${error.message}`,'error');}}}
|
||||
let adminDashboardInstance=null;document.addEventListener('DOMContentLoaded',function(){if(!adminDashboardInstance){adminDashboardInstance=new AdminDashboard();window.AdminDashboard=adminDashboardInstance;console.log('🎯 Admin Dashboard erfolgreich initialisiert (unified)');}});window.AdminDashboard=AdminDashboard;
|
BIN
backend/static/js/admin-unified.min.js.gz
Normal file
BIN
backend/static/js/advanced-components.js.gz
Normal file
79
backend/static/js/advanced-components.min.js
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
(function(){'use strict';window.MYP=window.MYP||{};window.MYP.Advanced=window.MYP.Advanced||{};class ProgressBar{constructor(container,options={}){this.container=typeof container==='string'?document.querySelector(container):container;this.options={value:0,max:100,showLabel:true,showPercentage:true,animated:true,color:'blue',size:'md',striped:false,...options};this.currentValue=this.options.value;this.init();}
|
||||
init(){if(!this.container){console.error('ProgressBar: Container nicht gefunden');return;}
|
||||
this.render();}
|
||||
render(){const percentage=Math.round((this.currentValue/this.options.max)*100);const sizeClass=this.getSizeClass();const colorClass=this.getColorClass();this.container.innerHTML=`<div class="progress-bar-container ${sizeClass}">${this.options.showLabel?`<div class="flex justify-between items-center mb-2"><span class="text-sm font-medium text-slate-700 dark:text-slate-300">${this.options.label||'Fortschritt'}</span>${this.options.showPercentage?`<span class="text-sm font-medium text-slate-700 dark:text-slate-300">${percentage}%</span>`:''}</div>`:''}<div class="progress-bar-track ${sizeClass}"><div class="progress-bar-fill ${colorClass} ${this.options.animated ? 'animated' : ''} ${this.options.striped ? 'striped' : ''}"
|
||||
style="width: ${percentage}%"
|
||||
role="progressbar"
|
||||
aria-valuenow="${this.currentValue}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="${this.options.max}"></div></div></div>`;}
|
||||
getSizeClass(){const sizes={'sm':'h-2','md':'h-3','lg':'h-4','xl':'h-6'};return sizes[this.options.size]||sizes.md;}
|
||||
getColorClass(){const colors={'blue':'bg-blue-500','green':'bg-green-500','red':'bg-red-500','yellow':'bg-yellow-500','purple':'bg-purple-500','indigo':'bg-indigo-500'};return colors[this.options.color]||colors.blue;}
|
||||
setValue(value,animate=true){const oldValue=this.currentValue;this.currentValue=Math.max(0,Math.min(this.options.max,value));if(animate){this.animateToValue(oldValue,this.currentValue);}else{this.render();}}
|
||||
animateToValue(from,to){const duration=500;const steps=60;const stepValue=(to-from)/steps;let currentStep=0;const animate=()=>{if(currentStep<steps){this.currentValue=from+(stepValue*currentStep);this.render();currentStep++;requestAnimationFrame(animate);}else{this.currentValue=to;this.render();}};animate();}
|
||||
increment(amount=1){this.setValue(this.currentValue+amount);}
|
||||
decrement(amount=1){this.setValue(this.currentValue-amount);}
|
||||
reset(){this.setValue(0);}
|
||||
complete(){this.setValue(this.options.max);}}
|
||||
class FileUpload{constructor(container,options={}){this.container=typeof container==='string'?document.querySelector(container):container;this.options={multiple:false,accept:'*/*',maxSize:50*1024*1024,maxFiles:10,dragDrop:true,showProgress:true,showPreview:true,uploadUrl:'/api/upload',chunkSize:1024*1024,...options};this.files=[];this.uploads=new Map();this.init();}
|
||||
init(){if(!this.container){console.error('FileUpload: Container nicht gefunden');return;}
|
||||
this.render();this.setupEventListeners();}
|
||||
render(){this.container.innerHTML=`<div class="file-upload-area"id="fileUploadArea"><div class="file-upload-dropzone ${this.options.dragDrop ? 'drag-enabled' : ''}"id="dropzone"><div class="text-center py-12"><svg class="mx-auto h-12 w-12 text-slate-400"stroke="currentColor"fill="none"viewBox="0 0 48 48"><path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"/></svg><div class="mt-4"><label for="fileInput"class="cursor-pointer"><span class="mt-2 block text-sm font-medium text-slate-900 dark:text-white">${this.options.dragDrop?'Dateien hierher ziehen oder':''}<span class="text-blue-600 dark:text-blue-400 hover:text-blue-500">durchsuchen</span></span></label><input type="file"
|
||||
id="fileInput"
|
||||
name="files"
|
||||
${this.options.multiple?'multiple':''}
|
||||
accept="${this.options.accept}"
|
||||
class="sr-only"></div><p class="mt-1 text-xs text-slate-500 dark:text-slate-400">${this.getFileTypeText()}• Max.${this.formatFileSize(this.options.maxSize)}</p></div></div><div class="file-list mt-4"id="fileList"></div></div>`;}
|
||||
setupEventListeners(){const fileInput=this.container.querySelector('#fileInput');const dropzone=this.container.querySelector('#dropzone');fileInput.addEventListener('change',(e)=>{this.handleFiles(Array.from(e.target.files));});if(this.options.dragDrop){dropzone.addEventListener('dragover',(e)=>{e.preventDefault();dropzone.classList.add('drag-over');});dropzone.addEventListener('dragleave',(e)=>{e.preventDefault();dropzone.classList.remove('drag-over');});dropzone.addEventListener('drop',(e)=>{e.preventDefault();dropzone.classList.remove('drag-over');this.handleFiles(Array.from(e.dataTransfer.files));});}}
|
||||
handleFiles(fileList){for(const file of fileList){if(this.validateFile(file)){this.addFile(file);}}
|
||||
this.renderFileList();}
|
||||
validateFile(file){if(file.size>this.options.maxSize){this.showError(`Datei"${file.name}"ist zu groß.Maximum:${this.formatFileSize(this.options.maxSize)}`);return false;}
|
||||
if(!this.options.multiple&&this.files.length>0){this.files=[];}else if(this.files.length>=this.options.maxFiles){this.showError(`Maximal ${this.options.maxFiles}Dateien erlaubt`);return false;}
|
||||
return true;}
|
||||
addFile(file){const fileData={id:this.generateId(),file:file,name:file.name,size:file.size,type:file.type,status:'pending',progress:0,error:null};this.files.push(fileData);if(this.options.showPreview&&file.type.startsWith('image/')){this.generatePreview(fileData);}}
|
||||
generatePreview(fileData){const reader=new FileReader();reader.onload=(e)=>{fileData.preview=e.target.result;this.renderFileList();};reader.readAsDataURL(fileData.file);}
|
||||
renderFileList(){const fileListContainer=this.container.querySelector('#fileList');if(this.files.length===0){fileListContainer.innerHTML='';return;}
|
||||
fileListContainer.innerHTML=this.files.map(fileData=>`<div class="file-item"data-file-id="${fileData.id}"><div class="flex items-center space-x-4 p-4 bg-slate-50 dark:bg-slate-800 rounded-lg">${fileData.preview?`<img src="${fileData.preview}"class="w-12 h-12 object-cover rounded"alt="Preview">`:`<div class="w-12 h-12 bg-slate-200 dark:bg-slate-700 rounded flex items-center justify-center"><svg class="w-6 h-6 text-slate-400"fill="currentColor"viewBox="0 0 20 20"><path fill-rule="evenodd"d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4z"clip-rule="evenodd"/></svg></div>`}<div class="flex-1 min-w-0"><div class="flex items-center justify-between"><p class="text-sm font-medium text-slate-900 dark:text-white truncate">${fileData.name}</p><button class="remove-file text-slate-400 hover:text-red-500"data-file-id="${fileData.id}"><svg class="w-4 h-4"fill="currentColor"viewBox="0 0 20 20"><path fill-rule="evenodd"d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"clip-rule="evenodd"/></svg></button></div><p class="text-xs text-slate-500 dark:text-slate-400">${this.formatFileSize(fileData.size)}• ${this.getStatusText(fileData.status)}</p>${this.options.showProgress&&fileData.status==='uploading'?`<div class="mt-2"><div class="w-full bg-slate-200 dark:bg-slate-600 rounded-full h-2"><div class="bg-blue-500 h-2 rounded-full transition-all duration-300"
|
||||
style="width: ${fileData.progress}%"></div></div></div>`:''}
|
||||
${fileData.error?`<p class="text-xs text-red-500 mt-1">${fileData.error}</p>`:''}</div></div></div>`).join('');fileListContainer.querySelectorAll('.remove-file').forEach(button=>{button.addEventListener('click',(e)=>{const fileId=e.target.closest('.remove-file').dataset.fileId;this.removeFile(fileId);});});}
|
||||
removeFile(fileId){this.files=this.files.filter(f=>f.id!==fileId);this.renderFileList();}
|
||||
async uploadFiles(){const pendingFiles=this.files.filter(f=>f.status==='pending');for(const fileData of pendingFiles){await this.uploadFile(fileData);}}
|
||||
async uploadFile(fileData){fileData.status='uploading';fileData.progress=0;this.renderFileList();try{const formData=new FormData();formData.append('file',fileData.file);const response=await fetch(this.options.uploadUrl,{method:'POST',body:formData,headers:{'X-CSRFToken':document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')||''}});if(response.ok){fileData.status='completed';fileData.progress=100;const result=await response.json();fileData.url=result.url;}else{throw new Error(`Upload fehlgeschlagen:${response.status}`);}}catch(error){fileData.status='error';fileData.error=error.message;}
|
||||
this.renderFileList();}
|
||||
getFileTypeText(){if(this.options.accept==='*/*')return'Alle Dateitypen';if(this.options.accept.includes('image/'))return'Bilder';if(this.options.accept.includes('.pdf'))return'PDF-Dateien';return'Spezifische Dateitypen';}
|
||||
getStatusText(status){const statusTexts={'pending':'Wartend','uploading':'Wird hochgeladen...','completed':'Abgeschlossen','error':'Fehler'};return statusTexts[status]||status;}
|
||||
formatFileSize(bytes){if(bytes===0)return'0 Bytes';const k=1024;const sizes=['Bytes','KB','MB','GB'];const i=Math.floor(Math.log(bytes)/Math.log(k));return parseFloat((bytes/Math.pow(k,i)).toFixed(2))+' '+sizes[i];}
|
||||
generateId(){return'file_'+Math.random().toString(36).substr(2,9);}
|
||||
showError(message){if(window.showToast){window.showToast(message,'error');}else{alert(message);}}
|
||||
getFiles(){return this.files;}
|
||||
getCompletedFiles(){return this.files.filter(f=>f.status==='completed');}
|
||||
clear(){this.files=[];this.renderFileList();}}
|
||||
class DatePicker{constructor(input,options={}){this.input=typeof input==='string'?document.querySelector(input):input;this.options={format:'dd.mm.yyyy',minDate:null,maxDate:null,disabledDates:[],language:'de',closeOnSelect:true,showWeekNumbers:false,...options};this.isOpen=false;this.currentDate=new Date();this.selectedDate=null;this.init();}
|
||||
init(){if(!this.input){console.error('DatePicker: Input-Element nicht gefunden');return;}
|
||||
this.setupInput();this.createCalendar();this.setupEventListeners();}
|
||||
setupInput(){this.input.setAttribute('readonly','true');this.input.classList.add('datepicker-input');this.container=document.createElement('div');this.container.className='datepicker-container relative';this.input.parentNode.insertBefore(this.container,this.input);this.container.appendChild(this.input);}
|
||||
createCalendar(){this.calendar=document.createElement('div');this.calendar.className='datepicker-calendar absolute top-full left-0 mt-1 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-600 rounded-lg shadow-lg z-50 hidden';this.calendar.innerHTML=this.renderCalendar();this.container.appendChild(this.calendar);}
|
||||
renderCalendar(){const year=this.currentDate.getFullYear();const month=this.currentDate.getMonth();const monthName=this.getMonthName(month);return`<div class="datepicker-header p-4 border-b border-slate-200 dark:border-slate-600"><div class="flex items-center justify-between"><button type="button"class="prev-month p-1 hover:bg-slate-100 dark:hover:bg-slate-700 rounded"><svg class="w-4 h-4"fill="currentColor"viewBox="0 0 20 20"><path fill-rule="evenodd"d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"clip-rule="evenodd"/></svg></button><div class="text-sm font-medium text-slate-900 dark:text-white">${monthName}${year}</div><button type="button"class="next-month p-1 hover:bg-slate-100 dark:hover:bg-slate-700 rounded"><svg class="w-4 h-4"fill="currentColor"viewBox="0 0 20 20"><path fill-rule="evenodd"d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"clip-rule="evenodd"/></svg></button></div></div><div class="datepicker-body p-4"><div class="grid grid-cols-7 gap-1 mb-2">${this.getWeekdayHeaders()}</div><div class="grid grid-cols-7 gap-1">${this.getDaysOfMonth(year,month)}</div></div>`;}
|
||||
getWeekdayHeaders(){const weekdays=['Mo','Di','Mi','Do','Fr','Sa','So'];return weekdays.map(day=>`<div class="text-xs font-medium text-slate-500 dark:text-slate-400 text-center p-1">${day}</div>`).join('');}
|
||||
getDaysOfMonth(year,month){const firstDay=new Date(year,month,1);const lastDay=new Date(year,month+1,0);const startDate=new Date(firstDay);startDate.setDate(startDate.getDate()-((firstDay.getDay()+6)%7));const days=[];const current=new Date(startDate);while(current<=lastDay||current.getMonth()===month){const isCurrentMonth=current.getMonth()===month;const isToday=this.isToday(current);const isSelected=this.isSelectedDate(current);const isDisabled=this.isDisabledDate(current);const classes=['w-8 h-8 text-sm rounded cursor-pointer flex items-center justify-center transition-colors',isCurrentMonth?'text-slate-900 dark:text-white':'text-slate-400 dark:text-slate-600',isToday?'bg-blue-100 dark:bg-blue-900 text-blue-900 dark:text-blue-100':'',isSelected?'bg-blue-500 text-white':'',!isDisabled&&isCurrentMonth?'hover:bg-slate-100 dark:hover:bg-slate-700':'',isDisabled?'cursor-not-allowed opacity-50':''].filter(Boolean);days.push(`<div class="${classes.join(' ')}"
|
||||
data-date="${this.formatDateForData(current)}"
|
||||
${isDisabled?'':'data-selectable="true"'}>${current.getDate()}</div>`);current.setDate(current.getDate()+1);if(days.length>=42)break;}
|
||||
return days.join('');}
|
||||
setupEventListeners(){this.input.addEventListener('click',()=>{this.toggle();});this.calendar.addEventListener('click',(e)=>{if(e.target.classList.contains('prev-month')){this.previousMonth();}else if(e.target.classList.contains('next-month')){this.nextMonth();}else if(e.target.dataset.selectable){this.selectDate(new Date(e.target.dataset.date));}});document.addEventListener('click',(e)=>{if(!this.container.contains(e.target)){this.close();}});}
|
||||
toggle(){if(this.isOpen){this.close();}else{this.open();}}
|
||||
open(){this.calendar.classList.remove('hidden');this.isOpen=true;this.updateCalendar();}
|
||||
close(){this.calendar.classList.add('hidden');this.isOpen=false;}
|
||||
selectDate(date){this.selectedDate=new Date(date);this.input.value=this.formatDate(date);this.input.dispatchEvent(new CustomEvent('dateselected',{detail:{date:new Date(date)}}));if(this.options.closeOnSelect){this.close();}}
|
||||
previousMonth(){this.currentDate.setMonth(this.currentDate.getMonth()-1);this.updateCalendar();}
|
||||
nextMonth(){this.currentDate.setMonth(this.currentDate.getMonth()+1);this.updateCalendar();}
|
||||
updateCalendar(){this.calendar.innerHTML=this.renderCalendar();}
|
||||
isToday(date){const today=new Date();return date.toDateString()===today.toDateString();}
|
||||
isSelectedDate(date){return this.selectedDate&&date.toDateString()===this.selectedDate.toDateString();}
|
||||
isDisabledDate(date){if(this.options.minDate&&date<this.options.minDate)return true;if(this.options.maxDate&&date>this.options.maxDate)return true;return this.options.disabledDates.some(disabled=>date.toDateString()===disabled.toDateString());}
|
||||
formatDate(date){const day=date.getDate().toString().padStart(2,'0');const month=(date.getMonth()+1).toString().padStart(2,'0');const year=date.getFullYear();return this.options.format.replace('dd',day).replace('mm',month).replace('yyyy',year);}
|
||||
formatDateForData(date){return date.toISOString().split('T')[0];}
|
||||
getMonthName(monthIndex){const months=['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'];return months[monthIndex];}
|
||||
setValue(date){if(date){this.selectDate(new Date(date));}}
|
||||
getValue(){return this.selectedDate;}
|
||||
clear(){this.selectedDate=null;this.input.value='';}}
|
||||
window.MYP.Advanced={ProgressBar,FileUpload,DatePicker,createProgressBar:(container,options)=>new ProgressBar(container,options),createFileUpload:(container,options)=>new FileUpload(container,options),createDatePicker:(input,options)=>new DatePicker(input,options)};document.addEventListener('DOMContentLoaded',function(){document.querySelectorAll('[data-datepicker]').forEach(input=>{const options=JSON.parse(input.dataset.datepicker||'{}');new DatePicker(input,options);});document.querySelectorAll('[data-file-upload]').forEach(container=>{const options=JSON.parse(container.dataset.fileUpload||'{}');new FileUpload(container,options);});console.log('🚀 MYP Advanced Components geladen');});})();
|
BIN
backend/static/js/advanced-components.min.js.gz
Normal file
BIN
backend/static/js/auto-logout.js.gz
Normal file
14
backend/static/js/auto-logout.min.js
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
class AutoLogoutManager{constructor(){this.timer=null;this.warningTimer=null;this.timeout=60;this.warningTime=5;this.isWarningShown=false;this.init();}
|
||||
async init(){await this.loadSettings();this.setupActivityListeners();this.startTimer();}
|
||||
async loadSettings(){try{const response=await fetch('/api/user/settings');if(response.ok){const data=await response.json();if(data.success&&data.settings.privacy?.auto_logout){const timeout=parseInt(data.settings.privacy.auto_logout);if(timeout>0&&timeout!=='never'){this.timeout=timeout;}else{this.timeout=0;}}}}catch(error){console.warn('Auto-Logout-Einstellungen konnten nicht geladen werden:',error);}}
|
||||
setupActivityListeners(){const events=['mousedown','mousemove','keypress','scroll','touchstart','click'];events.forEach(event=>{document.addEventListener(event,()=>this.resetTimer(),{passive:true});});}
|
||||
startTimer(){if(this.timeout<=0)return;this.clearTimers();const timeoutMs=this.timeout*60*1000;const warningMs=this.warningTime*60*1000;this.warningTimer=setTimeout(()=>this.showWarning(),timeoutMs-warningMs);this.timer=setTimeout(()=>this.performLogout(),timeoutMs);}
|
||||
resetTimer(){if(this.isWarningShown){this.closeWarning();}
|
||||
this.startTimer();}
|
||||
clearTimers(){if(this.timer)clearTimeout(this.timer);if(this.warningTimer)clearTimeout(this.warningTimer);}
|
||||
showWarning(){if(this.isWarningShown)return;this.isWarningShown=true;const modal=document.createElement('div');modal.id='auto-logout-warning';modal.className='fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';modal.innerHTML=`<div class="bg-white dark:bg-slate-800 rounded-lg p-6 max-w-md mx-4 shadow-xl"><h3 class="text-lg font-medium text-slate-900 dark:text-white mb-4">Automatische Abmeldung</h3><p class="text-sm text-slate-600 dark:text-slate-300 mb-4">Sie werden in ${this.warningTime}Minuten aufgrund von Inaktivität abgemeldet.</p><div class="flex space-x-3"><button id="stay-logged-in"class="bg-blue-600 text-white px-4 py-2 rounded-lg">Angemeldet bleiben</button><button id="logout-now"class="bg-gray-300 text-slate-700 px-4 py-2 rounded-lg">Jetzt abmelden</button></div></div>`;document.body.appendChild(modal);document.getElementById('stay-logged-in').onclick=()=>{this.closeWarning();this.sendKeepAlive();this.resetTimer();};document.getElementById('logout-now').onclick=()=>{this.performLogout();};}
|
||||
closeWarning(){const modal=document.getElementById('auto-logout-warning');if(modal)modal.remove();this.isWarningShown=false;}
|
||||
async sendKeepAlive(){try{await fetch('/api/auth/keep-alive',{method:'POST',headers:{'Content-Type':'application/json','X-CSRFToken':this.getCSRFToken()}});}catch(error){console.warn('Keep-Alive fehlgeschlagen:',error);}}
|
||||
getCSRFToken(){const metaTag=document.querySelector('meta[name="csrf-token"]');return metaTag?metaTag.getAttribute('content'):'';}
|
||||
async performLogout(){this.closeWarning();this.clearTimers();window.location.href='/auth/logout';}}
|
||||
document.addEventListener('DOMContentLoaded',function(){if(!window.location.pathname.includes('/login')){window.autoLogoutManager=new AutoLogoutManager();}});
|