feat: Implement frontend production deployment and enhance admin dashboard functionality

This commit is contained in:
2025-05-26 21:54:13 +02:00
parent c2ea6c34ea
commit 7aa70cf976
59 changed files with 9161 additions and 10894 deletions

View File

@@ -0,0 +1,237 @@
/* Enhanced Glassmorphism Effects for MYP Application */
/* Base Glass Effects */
.glass-base {
backdrop-filter: blur(20px) saturate(180%) brightness(110%);
-webkit-backdrop-filter: blur(20px) saturate(180%) brightness(110%);
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.glass-strong {
backdrop-filter: blur(24px) saturate(200%) brightness(120%);
-webkit-backdrop-filter: blur(24px) saturate(200%) brightness(120%);
box-shadow: 0 35px 60px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.glass-subtle {
backdrop-filter: blur(16px) saturate(150%) brightness(105%);
-webkit-backdrop-filter: blur(16px) saturate(150%) brightness(105%);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.05);
}
/* Light Mode Glass */
.glass-light {
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.3);
}
.glass-light-strong {
background: rgba(255, 255, 255, 0.6);
border: 1px solid rgba(255, 255, 255, 0.4);
}
/* Dark Mode Glass */
.glass-dark {
background: rgba(0, 0, 0, 0.7);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.05);
}
.glass-dark-strong {
background: rgba(0, 0, 0, 0.8);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow: 0 35px 60px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.08);
}
/* Interactive Glass Elements */
.glass-interactive {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
}
.glass-interactive:hover {
transform: translateY(-2px);
backdrop-filter: blur(28px) saturate(220%) brightness(125%);
-webkit-backdrop-filter: blur(28px) saturate(220%) brightness(125%);
box-shadow: 0 40px 80px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.15);
}
/* Glass Navigation */
.glass-nav {
background: rgba(255, 255, 255, 0.5);
backdrop-filter: blur(24px) saturate(200%) brightness(120%);
-webkit-backdrop-filter: blur(24px) saturate(200%) brightness(120%);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.dark .glass-nav {
background: rgba(0, 0, 0, 0.5);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.05);
}
/* Glass Cards */
.glass-card-enhanced {
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(20px) saturate(180%) brightness(110%);
-webkit-backdrop-filter: blur(20px) saturate(180%) brightness(110%);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 16px;
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
}
.dark .glass-card-enhanced {
background: rgba(0, 0, 0, 0.7);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.05);
}
.glass-card-enhanced:hover {
transform: translateY(-4px);
box-shadow: 0 35px 70px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.15);
}
.dark .glass-card-enhanced:hover {
box-shadow: 0 35px 70px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);
}
/* Glass Buttons */
.glass-btn {
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(16px) saturate(150%) brightness(110%);
-webkit-backdrop-filter: blur(16px) saturate(150%) brightness(110%);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 12px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.1);
transition: all 0.2s ease;
}
.dark .glass-btn {
background: rgba(0, 0, 0, 0.8);
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.08);
}
.glass-btn:hover {
transform: translateY(-1px);
backdrop-filter: blur(20px) saturate(170%) brightness(115%);
-webkit-backdrop-filter: blur(20px) saturate(170%) brightness(115%);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.15);
}
/* Glass Modals */
.glass-modal {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(32px) saturate(200%) brightness(115%);
-webkit-backdrop-filter: blur(32px) saturate(200%) brightness(115%);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 20px;
box-shadow: 0 50px 100px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.2);
}
.dark .glass-modal {
background: rgba(0, 0, 0, 0.85);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow: 0 50px 100px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.1);
}
/* Glass Form Elements */
.glass-input {
background: rgba(255, 255, 255, 0.6);
backdrop-filter: blur(16px) saturate(150%);
-webkit-backdrop-filter: blur(16px) saturate(150%);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 8px;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.05);
transition: all 0.2s ease;
}
.dark .glass-input {
background: rgba(0, 0, 0, 0.6);
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.05);
}
.glass-input:focus {
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.15), 0 0 0 2px rgba(59, 130, 246, 0.5);
}
/* 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%);
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);
}
.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);
}
/* 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 */
.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%);
}
.dark .glass-overlay {
background: linear-gradient(135deg, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.1) 100%);
}
/* Responsive glass effects */
@media (max-width: 768px) {
.glass-base,
.glass-strong,
.glass-card-enhanced {
backdrop-filter: blur(16px) saturate(150%);
-webkit-backdrop-filter: blur(16px) saturate(150%);
}
}
/* High contrast mode adjustments */
@media (prefers-contrast: high) {
.glass-base,
.glass-strong,
.glass-card-enhanced {
border-width: 2px;
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
}
/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
.glass-interactive,
.glass-card-enhanced,
.glass-btn {
transition: none;
}
.glass-float {
animation: none;
}
}

View File

@@ -40,18 +40,18 @@
/* Navbar Styles - Glassmorphism ohne überlagerte Hintergründe */
nav {
@apply bg-white/70 dark:bg-black/70 backdrop-blur-lg border-b border-gray-200/80 dark:border-slate-700/30 shadow-md transition-all duration-300;
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
@apply bg-white/60 dark:bg-black/60 backdrop-blur-xl border-b border-gray-200/70 dark:border-slate-700/20 shadow-lg transition-all duration-300;
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.05);
}
/* Benutzer-Dropdown Styles */
#user-dropdown {
@apply absolute right-0 mt-2 w-64 bg-white/70 dark:bg-black/70 backdrop-blur-lg border border-gray-200/80 dark:border-slate-700/30 rounded-xl shadow-xl transition-all duration-200 z-50;
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
@apply absolute right-0 mt-2 w-64 bg-white/60 dark:bg-black/60 backdrop-blur-xl border border-gray-200/70 dark:border-slate-700/20 rounded-xl shadow-2xl transition-all duration-200 z-50;
backdrop-filter: blur(20px) saturate(180%) brightness(110%);
-webkit-backdrop-filter: blur(20px) saturate(180%) brightness(110%);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.1);
animation: fadeIn 0.2s ease-out forwards;
}
}
@@ -84,7 +84,10 @@
}
.stat-card {
@apply bg-white dark:bg-dark-surface rounded-xl border border-gray-200 dark:border-slate-700/30 p-5 relative overflow-hidden shadow-lg hover:shadow-xl transition-all duration-300 hover:-translate-y-1;
@apply bg-white/60 dark:bg-black/70 rounded-xl border border-gray-200/60 dark:border-slate-700/30 p-5 relative overflow-hidden shadow-2xl hover:shadow-2xl transition-all duration-300 hover:-translate-y-1 backdrop-blur-xl;
backdrop-filter: blur(20px) saturate(180%) brightness(110%);
-webkit-backdrop-filter: blur(20px) saturate(180%) brightness(110%);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.stat-icon {
@@ -141,7 +144,10 @@
.form-input,
.form-select,
.form-textarea {
@apply w-full px-3 py-2 bg-white dark:bg-slate-800 border border-gray-300 dark:border-slate-600 rounded-lg text-slate-900 dark:text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-slate-500 focus:border-transparent transition-all duration-200;
@apply w-full px-3 py-2 bg-white/60 dark:bg-slate-800/60 border border-gray-300/60 dark:border-slate-600/60 rounded-lg text-slate-900 dark:text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-slate-500 focus:border-transparent transition-all duration-200 backdrop-blur-lg;
backdrop-filter: blur(16px) saturate(150%);
-webkit-backdrop-filter: blur(16px) saturate(150%);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.05);
}
/* Tabellen im Admin Panel */
@@ -192,7 +198,10 @@
/* Drucker-Karten */
.printer-card {
@apply bg-white dark:bg-dark-surface rounded-xl border border-gray-200 dark:border-slate-700/30 p-6 shadow-lg hover:shadow-xl transition-all duration-300 hover:-translate-y-1;
@apply bg-white/60 dark:bg-black/70 rounded-xl border border-gray-200/60 dark:border-slate-700/30 p-6 shadow-2xl hover:shadow-2xl transition-all duration-300 hover:-translate-y-1 backdrop-blur-xl;
backdrop-filter: blur(20px) saturate(180%) brightness(110%);
-webkit-backdrop-filter: blur(20px) saturate(180%) brightness(110%);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.printer-header {
@@ -345,25 +354,27 @@
/* Glassmorphism Flash Messages */
.flash-message {
@apply bg-white/70 dark:bg-black/70 backdrop-blur-md border border-gray-200 dark:border-slate-700/50 rounded-xl p-4 relative mb-4 shadow-md;
animation: slide-down 0.3s ease-out forwards;
transition: all 0.3s ease;
@apply fixed top-4 right-4 px-6 py-4 rounded-xl text-sm font-medium shadow-2xl transform transition-all duration-300 z-50 backdrop-blur-xl border border-white/20;
backdrop-filter: blur(20px) saturate(180%) brightness(120%);
-webkit-backdrop-filter: blur(20px) saturate(180%) brightness(120%);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.1);
animation: slide-down 0.3s ease-out;
}
.flash-message.info {
@apply border-l-4 border-blue-500;
@apply bg-blue-500/70 dark:bg-blue-600/70 text-white;
}
.flash-message.success {
@apply border-l-4 border-green-500;
@apply bg-green-500/70 dark:bg-green-600/70 text-white;
}
.flash-message.warning {
@apply border-l-4 border-yellow-500;
@apply bg-yellow-500/70 dark:bg-yellow-600/70 text-white;
}
.flash-message.error {
@apply border-l-4 border-red-500;
@apply bg-red-500/70 dark:bg-red-600/70 text-white;
}
@keyframes slide-down {
@@ -404,26 +415,41 @@
@layer components {
/* Buttons im Light Mode Schwarz statt Blau */
.btn-primary {
@apply bg-black hover:bg-gray-800 dark:bg-white dark:hover:bg-gray-200 text-white dark:text-slate-900 px-4 py-2 rounded-lg transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 shadow-sm;
@apply bg-black/80 hover:bg-gray-800/80 dark:bg-white/80 dark:hover:bg-gray-200/80 text-white dark:text-slate-900 px-4 py-2 rounded-lg transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 shadow-xl backdrop-blur-lg;
backdrop-filter: blur(16px) saturate(150%) brightness(110%);
-webkit-backdrop-filter: blur(16px) saturate(150%) brightness(110%);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.btn-secondary {
@apply bg-white hover:bg-gray-100 dark:bg-slate-800 dark:hover:bg-slate-700 text-slate-900 dark:text-white border border-gray-300 dark:border-slate-700 px-4 py-2 rounded-lg transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-slate-500 focus:ring-offset-2 shadow-sm;
@apply bg-white/70 hover:bg-gray-100/70 dark:bg-slate-800/70 dark:hover:bg-slate-700/70 text-slate-900 dark:text-white border border-gray-300/60 dark:border-slate-700/60 px-4 py-2 rounded-lg transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-slate-500 focus:ring-offset-2 shadow-xl backdrop-blur-lg;
backdrop-filter: blur(16px) saturate(150%) brightness(110%);
-webkit-backdrop-filter: blur(16px) saturate(150%) brightness(110%);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.btn-outline {
@apply border-2 border-black hover:bg-black dark:border-white dark:hover:bg-white text-black hover:text-white dark:text-white dark:hover:text-slate-900 px-4 py-2 rounded-lg transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2;
@apply border-2 border-black/70 hover:bg-black/70 dark:border-white/70 dark:hover:bg-white/70 text-black hover:text-white dark:text-white dark:hover:text-slate-900 px-4 py-2 rounded-lg transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 backdrop-blur-lg;
backdrop-filter: blur(16px) saturate(150%);
-webkit-backdrop-filter: blur(16px) saturate(150%);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.05);
}
/* Glassmorphism Card mit abgerundeten Ecken */
.glass-card {
@apply bg-white/80 dark:bg-black/80 backdrop-blur-md border border-gray-200 dark:border-slate-700/50 rounded-xl p-6 shadow-lg transition-all duration-300;
@apply bg-white/70 dark:bg-black/70 backdrop-blur-xl border border-gray-200/60 dark:border-slate-700/40 rounded-xl p-6 shadow-xl transition-all duration-300;
backdrop-filter: blur(20px) saturate(180%) brightness(110%);
-webkit-backdrop-filter: blur(20px) saturate(180%) brightness(110%);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.1);
border-radius: var(--card-radius);
}
/* Dashboard Cards mit schwarzem Hintergrund */
.dashboard-card {
@apply bg-white/70 dark:bg-black/90 backdrop-blur-lg border border-gray-200/80 dark:border-slate-700/30 rounded-xl p-6 shadow-lg transition-all duration-300;
@apply bg-white/60 dark:bg-black/80 backdrop-blur-2xl border border-gray-200/70 dark:border-slate-700/20 rounded-xl p-6 shadow-xl transition-all duration-300;
backdrop-filter: blur(24px) saturate(200%) brightness(120%);
-webkit-backdrop-filter: blur(24px) saturate(200%) brightness(120%);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.1);
border-radius: var(--card-radius);
}
@@ -438,16 +464,16 @@
/* Verbesserte Navbar Styles - Glassmorphism ohne überlagerte Hintergründe */
.navbar {
@apply sticky top-0 z-50 backdrop-blur-xl border-b border-gray-200/50 dark:border-slate-700/20 shadow-lg;
background: rgba(255, 255, 255, 0.6);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.15);
@apply sticky top-0 z-50 backdrop-blur-2xl border-b border-gray-200/40 dark:border-slate-700/15 shadow-xl;
background: rgba(255, 255, 255, 0.5);
backdrop-filter: blur(24px) saturate(200%) brightness(120%);
-webkit-backdrop-filter: blur(24px) saturate(200%) brightness(120%);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.dark .navbar {
background: rgba(0, 0, 0, 0.6); /* Transparenter für stärkeren Glaseffekt */
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
background: rgba(0, 0, 0, 0.5); /* Transparenter für stärkeren Glaseffekt */
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.05);
}
.navbar-brand {
@@ -495,7 +521,10 @@
/* Dropdown Styles */
.user-dropdown {
@apply absolute right-0 mt-2 w-64 bg-white/90 dark:bg-black/90 backdrop-blur-xl border border-gray-200/80 dark:border-slate-700/30 rounded-xl shadow-xl z-50 overflow-hidden;
@apply absolute right-0 mt-2 w-64 bg-white/70 dark:bg-black/80 backdrop-blur-2xl border border-gray-200/70 dark:border-slate-700/20 rounded-xl shadow-2xl z-50 overflow-hidden;
backdrop-filter: blur(24px) saturate(200%) brightness(120%);
-webkit-backdrop-filter: blur(24px) saturate(200%) brightness(120%);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(255, 255, 255, 0.1);
animation: fadeIn 0.2s ease-out forwards;
}
@@ -528,9 +557,10 @@
/* Dark Mode Toggle - Schwarz statt Blau im Light Mode */
.dark-mode-toggle {
@apply p-3 rounded-full bg-black/90 hover:bg-gray-800/90 dark:bg-white/90 dark:hover:bg-gray-200/90 text-white dark:text-slate-900 transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-gray-500 shadow-md;
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
@apply p-3 rounded-full bg-black/80 hover:bg-gray-800/80 dark:bg-white/80 dark:hover:bg-gray-200/80 text-white dark:text-slate-900 transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-gray-500 shadow-xl;
backdrop-filter: blur(12px) saturate(150%);
-webkit-backdrop-filter: blur(12px) saturate(150%);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.1);
min-width: 42px;
min-height: 42px;
display: flex;
@@ -550,28 +580,38 @@
/* Dashboard Stat Cards mit schwarzem Hintergrund im Dark Mode */
.mb-stat-card {
background: linear-gradient(135deg, #f0f9ff 0%, #e6f2ff 100%);
background: linear-gradient(135deg, rgba(240, 249, 255, 0.6) 0%, rgba(230, 242, 255, 0.6) 100%);
color: #0f172a;
position: relative;
overflow: hidden;
border: none;
border-radius: var(--card-radius);
backdrop-filter: blur(20px) saturate(180%) brightness(110%);
-webkit-backdrop-filter: blur(20px) saturate(180%) brightness(110%);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.dark .mb-stat-card {
background: linear-gradient(135deg, #000000 0%, #0a0a0a 100%); /* Noch dunkleres Schwarz */
background: linear-gradient(135deg, rgba(0, 0, 0, 0.7) 0%, rgba(10, 10, 10, 0.7) 100%); /* Noch dunkleres Schwarz */
color: var(--text-primary, #f8fafc);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.05);
}
/* Stats und Jobs Page Card Styles */
.stats-card, .job-card {
@apply bg-white/70 dark:bg-black/90 backdrop-blur-lg border border-gray-200/80 dark:border-slate-700/30 rounded-xl shadow-lg transition-all duration-300;
@apply bg-white/60 dark:bg-black/80 backdrop-blur-2xl border border-gray-200/70 dark:border-slate-700/20 rounded-xl shadow-2xl transition-all duration-300;
backdrop-filter: blur(24px) saturate(200%) brightness(120%);
-webkit-backdrop-filter: blur(24px) saturate(200%) brightness(120%);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.1);
border-radius: var(--card-radius);
}
/* Footer Styling */
footer {
@apply bg-white/70 dark:bg-black/70 backdrop-blur-lg border-t border-gray-200/80 dark:border-slate-700/30 transition-all duration-300;
@apply bg-white/60 dark:bg-black/60 backdrop-blur-xl border-t border-gray-200/70 dark:border-slate-700/20 transition-all duration-300;
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.05);
}
/* Dropdown Pfeil Animation */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
<!-- Das ist eine Platzhalter-Datei für das Apple Touch Icon -->
<!-- Diese sollte durch ein echtes PNG-Icon ersetzt werden -->
<!-- Kopiere icon-144x144.png zu apple-touch-icon.png -->

View File

@@ -0,0 +1,391 @@
/**
* Mercedes-Benz MYP Admin Live Dashboard
* Echtzeit-Updates für das Admin Panel mit echten Daten
*/
class AdminLiveDashboard {
constructor() {
this.isLive = false;
this.updateInterval = null;
this.retryCount = 0;
this.maxRetries = 3;
// Dynamische API-Base-URL-Erkennung
this.apiBaseUrl = this.detectApiBaseUrl();
console.log('🔗 API Base URL erkannt:', this.apiBaseUrl);
this.init();
}
detectApiBaseUrl() {
// Versuche verschiedene Ports zu erkennen
const currentHost = window.location.hostname;
const currentPort = window.location.port;
const currentProtocol = window.location.protocol;
console.log('🔍 Aktuelle URL-Informationen:', {
host: currentHost,
port: currentPort,
protocol: currentProtocol,
fullUrl: window.location.href
});
// Wenn wir bereits auf dem richtigen Port sind, verwende relative URLs
if (currentPort === '5000' || !currentPort) {
console.log('✅ Verwende relative URLs (gleicher Port oder Standard)');
return '';
}
// Wenn wir auf 8443 sind, versuche 5000 (häufiger Fall)
if (currentPort === '8443') {
const fallbackUrl = `http://${currentHost}:5000`;
console.log('🔄 Fallback von HTTPS:8443 zu HTTP:5000:', fallbackUrl);
return fallbackUrl;
}
// Für andere Ports, verwende Standard-Backend-Port
const defaultPort = currentProtocol === 'https:' ? '8443' : '5000';
const fallbackUrl = `${currentProtocol}//${currentHost}:${defaultPort}`;
console.log('🔄 Standard-Fallback:', fallbackUrl);
return fallbackUrl;
}
init() {
console.log('🚀 Mercedes-Benz MYP Admin Live Dashboard gestartet');
// Live-Status anzeigen
this.updateLiveTime();
this.startLiveUpdates();
// Event Listeners
this.bindEvents();
// Initial Load
this.loadLiveStats();
}
bindEvents() {
// Quick Action Buttons
const systemStatusBtn = document.getElementById('system-status-btn');
const analyticsBtn = document.getElementById('analytics-btn');
const maintenanceBtn = document.getElementById('maintenance-btn');
if (systemStatusBtn) {
systemStatusBtn.addEventListener('click', () => this.showSystemStatus());
}
if (analyticsBtn) {
analyticsBtn.addEventListener('click', () => this.showAnalytics());
}
if (maintenanceBtn) {
maintenanceBtn.addEventListener('click', () => this.showMaintenance());
}
// Page Visibility API für optimierte Updates
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
this.pauseLiveUpdates();
} else {
this.resumeLiveUpdates();
}
});
}
startLiveUpdates() {
this.isLive = true;
this.updateLiveIndicator(true);
// Live Stats alle 30 Sekunden aktualisieren
this.updateInterval = setInterval(() => {
this.loadLiveStats();
}, 30000);
// Zeit jede Sekunde aktualisieren
setInterval(() => {
this.updateLiveTime();
}, 1000);
}
pauseLiveUpdates() {
this.isLive = false;
this.updateLiveIndicator(false);
if (this.updateInterval) {
clearInterval(this.updateInterval);
}
}
resumeLiveUpdates() {
if (!this.isLive) {
this.startLiveUpdates();
this.loadLiveStats(); // Sofortiges Update beim Fortsetzen
}
}
updateLiveIndicator(isLive) {
const indicator = document.getElementById('live-indicator');
if (indicator) {
if (isLive) {
indicator.className = 'w-2 h-2 bg-green-400 rounded-full animate-pulse';
} else {
indicator.className = 'w-2 h-2 bg-gray-400 rounded-full';
}
}
}
updateLiveTime() {
const timeElement = document.getElementById('live-time');
if (timeElement) {
const now = new Date();
timeElement.textContent = now.toLocaleTimeString('de-DE');
}
}
async loadLiveStats() {
try {
const url = `${this.apiBaseUrl}/api/admin/stats/live`;
console.log('🔄 Lade Live-Statistiken von:', url);
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.getCSRFToken()
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (data.success) {
this.updateStatsDisplay(data);
this.retryCount = 0; // Reset retry count on success
// Success notification (optional)
this.showQuietNotification('Live-Daten aktualisiert', 'success');
} else {
throw new Error(data.error || 'Unbekannter Fehler beim Laden der Live-Statistiken');
}
} catch (error) {
console.error('Fehler beim Laden der Live-Statistiken:', error);
this.retryCount++;
if (this.retryCount <= this.maxRetries) {
console.log(`Versuche erneut... (${this.retryCount}/${this.maxRetries})`);
setTimeout(() => this.loadLiveStats(), 5000); // Retry nach 5 Sekunden
} else {
this.handleConnectionError();
}
}
}
updateStatsDisplay(data) {
// Benutzer Stats
this.updateCounter('live-users-count', data.total_users);
this.updateProgress('users-progress', Math.min((data.total_users / 20) * 100, 100)); // Max 20 users = 100%
// Drucker Stats
this.updateCounter('live-printers-count', data.total_printers);
this.updateElement('live-printers-online', `${data.online_printers} online`);
if (data.total_printers > 0) {
this.updateProgress('printers-progress', (data.online_printers / data.total_printers) * 100);
}
// Jobs Stats
this.updateCounter('live-jobs-active', data.active_jobs);
this.updateElement('live-jobs-queued', `${data.queued_jobs} in Warteschlange`);
this.updateProgress('jobs-progress', Math.min(data.active_jobs * 20, 100)); // Max 5 jobs = 100%
// Erfolgsrate Stats
this.updateCounter('live-success-rate', `${data.success_rate}%`);
this.updateProgress('success-progress', data.success_rate);
// Trend Analysis
this.updateSuccessTrend(data.success_rate);
console.log('📊 Live-Statistiken aktualisiert:', data);
}
updateCounter(elementId, newValue) {
const element = document.getElementById(elementId);
if (element) {
const currentValue = parseInt(element.textContent) || 0;
if (currentValue !== newValue) {
this.animateCounter(element, currentValue, newValue);
}
}
}
animateCounter(element, from, to) {
const duration = 1000; // 1 Sekunde
const increment = (to - from) / (duration / 16); // 60 FPS
let current = from;
const timer = setInterval(() => {
current += increment;
if ((increment > 0 && current >= to) || (increment < 0 && current <= to)) {
current = to;
clearInterval(timer);
}
element.textContent = Math.round(current);
}, 16);
}
updateElement(elementId, newValue) {
const element = document.getElementById(elementId);
if (element && element.textContent !== newValue) {
element.textContent = newValue;
}
}
updateProgress(elementId, percentage) {
const element = document.getElementById(elementId);
if (element) {
element.style.width = `${Math.max(0, Math.min(100, percentage))}%`;
}
}
updateSuccessTrend(successRate) {
const trendElement = document.getElementById('success-trend');
if (trendElement) {
let trendText = 'Stabil';
let trendClass = 'text-green-500';
let trendIcon = 'M5 10l7-7m0 0l7 7m-7-7v18'; // Up arrow
if (successRate >= 95) {
trendText = 'Excellent';
trendClass = 'text-green-600';
} else if (successRate >= 80) {
trendText = 'Gut';
trendClass = 'text-green-500';
} else if (successRate >= 60) {
trendText = 'Mittel';
trendClass = 'text-yellow-500';
trendIcon = 'M5 12h14'; // Horizontal line
} else {
trendText = 'Niedrig';
trendClass = 'text-red-500';
trendIcon = 'M19 14l-7 7m0 0l-7-7m7 7V3'; // Down arrow
}
trendElement.className = `text-sm ${trendClass}`;
trendElement.innerHTML = `
<span class="inline-flex items-center">
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${trendIcon}"/>
</svg>
${trendText}
</span>
`;
}
}
showSystemStatus() {
// System Status Modal oder Navigation
console.log('🔧 System Status angezeigt');
this.showNotification('System Status wird geladen...', 'info');
// Hier könnten weitere System-Details geladen werden
const url = `${this.apiBaseUrl}/api/admin/system/status`;
fetch(url)
.then(response => response.json())
.then(data => {
// System Status anzeigen
console.log('System Status:', data);
})
.catch(error => {
console.error('Fehler beim Laden des System Status:', error);
});
}
showAnalytics() {
console.log('📈 Live Analytics angezeigt');
this.showNotification('Analytics werden geladen...', 'info');
// Analytics Tab aktivieren oder Modal öffnen
const analyticsTab = document.querySelector('a[href*="tab=system"]');
if (analyticsTab) {
analyticsTab.click();
}
}
showMaintenance() {
console.log('🛠️ Wartung angezeigt');
this.showNotification('Wartungsoptionen werden geladen...', 'info');
// Wartungs-Tab aktivieren oder Modal öffnen
const systemTab = document.querySelector('a[href*="tab=system"]');
if (systemTab) {
systemTab.click();
}
}
handleConnectionError() {
console.error('🔴 Verbindung zu Live-Updates verloren');
this.updateLiveIndicator(false);
this.showNotification('Verbindung zu Live-Updates verloren. Versuche erneut...', 'error');
// Auto-Recovery nach 30 Sekunden
setTimeout(() => {
this.retryCount = 0;
this.loadLiveStats();
}, 30000);
}
showNotification(message, type = 'info') {
// Erstelle oder aktualisiere Notification
let notification = document.getElementById('live-notification');
if (!notification) {
notification = document.createElement('div');
notification.id = 'live-notification';
notification.className = 'fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg max-w-sm';
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 ${colors[type]} transform transition-all duration-300 translate-x-0`;
notification.textContent = message;
// Auto-Hide nach 3 Sekunden
setTimeout(() => {
if (notification) {
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
if (notification && notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}
}, 3000);
}
showQuietNotification(message, type) {
// Nur in der Konsole loggen für nicht-störende Updates
const emoji = type === 'success' ? '✅' : type === 'error' ? '❌' : '';
console.log(`${emoji} ${message}`);
}
getCSRFToken() {
const meta = document.querySelector('meta[name="csrf-token"]');
return meta ? meta.getAttribute('content') : '';
}
}
// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', function() {
new AdminLiveDashboard();
});
// Export for global access
window.AdminLiveDashboard = AdminLiveDashboard;

File diff suppressed because it is too large Load Diff

View File

@@ -12,17 +12,17 @@
"categories": ["productivity", "business"],
"icons": [
{
"src": "static/icons/mercedes-logo.svg",
"src": "icons/mercedes-logo.svg",
"sizes": "192x192",
"type": "image/svg+xml"
},
{
"src": "static/icons/mercedes-logo.svg",
"src": "icons/mercedes-logo.svg",
"sizes": "512x512",
"type": "image/svg+xml"
},
{
"src": "static/icons/icon-144x144.png",
"src": "icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png",
"purpose": "maskable any"