diff --git a/backend/app/blueprints/guest.py b/backend/app/blueprints/guest.py index d7de2ea9f..6b4121f8d 100644 --- a/backend/app/blueprints/guest.py +++ b/backend/app/blueprints/guest.py @@ -247,16 +247,23 @@ def api_start_job_with_otp(otp): """Job mit OTP starten.""" try: with get_cached_session() as db_session: - # OTP validieren und Job finden - guest_request = db_session.query(GuestRequest).filter_by(otp_code=otp).first() - if not guest_request: + # Alle genehmigten Anfragen mit OTP durchsuchen + guest_requests = db_session.query(GuestRequest).filter_by(status="approved").all() + + valid_request = None + for req in guest_requests: + if req.verify_otp(otp): + valid_request = req + break + + if not valid_request: return jsonify({"error": "Ungültiger oder abgelaufener Code"}), 400 - if not guest_request.job_id: + if not valid_request.job_id: return jsonify({"error": "Kein Job mit diesem Code verknüpft"}), 400 # Job laden - job = db_session.query(Job).filter_by(id=guest_request.job_id).first() + job = db_session.query(Job).filter_by(id=valid_request.job_id).first() if not job: return jsonify({"error": "Job nicht gefunden"}), 404 @@ -264,22 +271,37 @@ def api_start_job_with_otp(otp): if job.status != "scheduled": return jsonify({"error": "Job kann nicht gestartet werden"}), 400 + # Grace-Period prüfen (5 Minuten vor bis 5 Minuten nach geplantem Start) + now = datetime.now() + start_time = job.start_at + grace_start = start_time - timedelta(minutes=5) + grace_end = start_time + timedelta(minutes=5) + + if now < grace_start: + return jsonify({ + "error": f"Der Job kann erst ab {grace_start.strftime('%H:%M')} Uhr gestartet werden" + }), 400 + + if now > job.end_at: + return jsonify({"error": "Der Job ist bereits abgelaufen"}), 400 + # Job starten job.status = "active" - job.start_at = datetime.now() + job.start_at = now # Aktualisiere Startzeit auf jetzt - # OTP als verwendet markieren (optional: OTP löschen) - guest_request.otp_used_at = datetime.now() + # OTP als verwendet markieren + valid_request.otp_used_at = now db_session.commit() - logger.info(f"Job {job.id} mit OTP {otp} gestartet") + logger.info(f"Job {job.id} mit OTP {otp} gestartet von IP: {request.remote_addr}") return jsonify({ "success": True, "job_id": job.id, "status": job.status, - "started_at": job.start_at.isoformat() + "started_at": job.start_at.isoformat(), + "end_at": job.end_at.isoformat() }) except Exception as e: diff --git a/backend/app/database/myp.db-shm b/backend/app/database/myp.db-shm index 0a7987d9f..4bb8adb4d 100644 Binary files a/backend/app/database/myp.db-shm and b/backend/app/database/myp.db-shm differ diff --git a/backend/app/database/myp.db-wal b/backend/app/database/myp.db-wal index 390c435ce..5d96760bd 100644 Binary files a/backend/app/database/myp.db-wal and b/backend/app/database/myp.db-wal differ diff --git a/backend/app/migrate_db.py b/backend/app/migrate_db.py index a9a147f25..d36325ba7 100644 --- a/backend/app/migrate_db.py +++ b/backend/app/migrate_db.py @@ -5,6 +5,7 @@ Datenbank-Migrationsskript für Guest-Requests, UserPermissions und Notification import os import sys +import sqlite3 from datetime import datetime # Pfad zur App hinzufügen @@ -15,6 +16,61 @@ from utils.logging_config import get_logger logger = get_logger("migrate") +def column_exists(cursor, table_name, column_name): + """Prüft, ob eine Spalte in einer Tabelle existiert.""" + cursor.execute(f"PRAGMA table_info({table_name})") + columns = [row[1] for row in cursor.fetchall()] + return column_name in columns + +def get_database_path(): + """Ermittelt den Pfad zur Datenbankdatei.""" + db_path = os.path.join('database', 'app.db') + if not os.path.exists(db_path): + # Fallback für alternative Pfade + alternative_paths = [ + 'app.db', + '../database/app.db', + './database/app.db' + ] + for path in alternative_paths: + if os.path.exists(path): + db_path = path + break + return db_path + +def migrate_guest_requests_table(): + """Migriert die guest_requests Tabelle für neue Spalten.""" + db_path = get_database_path() + + if not os.path.exists(db_path): + logger.warning(f"Datenbankdatei nicht gefunden: {db_path}") + return False + + try: + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + # Prüfen ob otp_used_at Spalte bereits existiert + if not column_exists(cursor, 'guest_requests', 'otp_used_at'): + cursor.execute(""" + ALTER TABLE guest_requests + ADD COLUMN otp_used_at DATETIME + """) + logger.info("Spalte 'otp_used_at' zur guest_requests Tabelle hinzugefügt") + else: + logger.info("Spalte 'otp_used_at' existiert bereits") + + conn.commit() + conn.close() + return True + + except Exception as e: + logger.error(f"Fehler bei der Migration der guest_requests Tabelle: {str(e)}") + if 'conn' in locals(): + conn.rollback() + conn.close() + return False + def main(): """Führt die Datenbank-Migration aus.""" try: @@ -23,6 +79,10 @@ def main(): # Datenbank initialisieren (erstellt neue Tabellen) init_db() + # Spezifische Spalten-Migrationen + logger.info("Führe spezifische Tabellen-Migrationen aus...") + migrate_guest_requests_table() + logger.info("Datenbank-Migration erfolgreich abgeschlossen") # Testen, ob die neuen Tabellen funktionieren diff --git a/backend/app/static/css/input.css b/backend/app/static/css/input.css index cd8f68cdf..52c6ef25d 100644 --- a/backend/app/static/css/input.css +++ b/backend/app/static/css/input.css @@ -2975,365 +2975,493 @@ footer { .printer-card-new.online { @apply bg-gradient-to-br from-green-50/90 to-emerald-50/80 dark:from-green-900/30 dark:to-emerald-900/20 border-green-200 dark:border-green-700/50; box-shadow: - 0 20px 40px rgba(34, 197, 94, 0.15), - 0 10px 20px rgba(34, 197, 94, 0.1), - 0 0 0 1px rgba(34, 197, 94, 0.2); + 0 20px 40px rgba(0, 122, 85, 0.08), + 0 10px 20px rgba(0, 122, 85, 0.06), + 0 0 0 1px rgba(209, 250, 229, 0.4); } .dark .printer-card-new.online { box-shadow: - 0 20px 40px rgba(34, 197, 94, 0.3), - 0 10px 20px rgba(34, 197, 94, 0.2), - 0 0 0 1px rgba(34, 197, 94, 0.15); + 0 20px 40px rgba(0, 0, 0, 0.3), + 0 10px 20px rgba(0, 0, 0, 0.2), + 0 0 0 1px rgba(16, 185, 129, 0.2); } -/* Offline Drucker-Karten */ -.printer-card-new.offline { - @apply bg-gradient-to-br from-red-50/90 to-rose-50/80 dark:from-red-900/30 dark:to-rose-900/20 border-red-200 dark:border-red-700/50; - box-shadow: - 0 20px 40px rgba(239, 68, 68, 0.15), - 0 10px 20px rgba(239, 68, 68, 0.1), - 0 0 0 1px rgba(239, 68, 68, 0.2); +/* Status-Badge mit verbesserten Styles */ +.status-badge-new { + @apply px-2.5 py-1 rounded-full text-xs font-medium inline-flex items-center space-x-1 shadow-sm; + background: rgba(255, 255, 255, 0.9); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); } -.dark .printer-card-new.offline { - box-shadow: - 0 20px 40px rgba(239, 68, 68, 0.3), - 0 10px 20px rgba(239, 68, 68, 0.2), - 0 0 0 1px rgba(239, 68, 68, 0.15); +.dark .status-badge-new { + background: rgba(30, 41, 59, 0.7); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); } -/* Hover-Effekte für Drucker-Karten */ -.printer-card-new:hover { - transform: translateY(-4px); - box-shadow: - 0 30px 60px rgba(0, 0, 0, 0.12), - 0 15px 30px rgba(0, 0, 0, 0.08), - 0 0 0 1px rgba(255, 255, 255, 0.15); +.status-badge-new.online { + @apply bg-green-100/90 text-green-800 dark:bg-green-900/60 dark:text-green-300; } -.dark .printer-card-new:hover { +.status-badge-new.offline { + @apply bg-red-100/90 text-red-800 dark:bg-red-900/60 dark:text-red-300; +} + +/* Verbesserte Filterleiste */ +.filter-bar-new { + @apply bg-white/80 dark:bg-slate-800/80 backdrop-blur-xl rounded-lg p-1.5 border border-gray-200/60 dark:border-slate-700/30 shadow-xl; box-shadow: - 0 30px 60px rgba(0, 0, 0, 0.5), - 0 15px 30px rgba(0, 0, 0, 0.4), + 0 10px 25px rgba(0, 0, 0, 0.05), + 0 5px 10px rgba(0, 0, 0, 0.03), + 0 0 0 1px rgba(255, 255, 255, 0.2); +} + +.dark .filter-bar-new { + box-shadow: + 0 10px 25px rgba(0, 0, 0, 0.2), + 0 5px 10px rgba(0, 0, 0, 0.15), + 0 0 0 1px rgba(255, 255, 255, 0.05); +} + +.filter-btn-new { + @apply px-3.5 py-2 text-sm rounded-md transition-all duration-300 font-medium; +} + +.filter-btn-new.active { + @apply bg-black text-white dark:bg-white dark:text-slate-900 shadow-md; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); +} + +.dark .filter-btn-new.active { + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); +} + +/* Verbesserte Aktionsschaltflächen */ +.action-btn-new { + @apply flex items-center justify-center gap-2 px-4 py-2.5 rounded-lg font-medium text-sm transition-all duration-300 shadow-md hover:-translate-y-0.5; + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); +} + +.action-btn-new.primary { + @apply bg-indigo-600 hover:bg-indigo-700 text-white dark:bg-indigo-600 dark:hover:bg-indigo-500; + box-shadow: 0 5px 15px rgba(79, 70, 229, 0.2); +} + +.dark .action-btn-new.primary { + box-shadow: 0 5px 15px rgba(79, 70, 229, 0.3); +} + +.action-btn-new.success { + @apply bg-green-600 hover:bg-green-700 text-white dark:bg-green-600 dark:hover:bg-green-500; + box-shadow: 0 5px 15px rgba(16, 185, 129, 0.2); +} + +.dark .action-btn-new.success { + box-shadow: 0 5px 15px rgba(16, 185, 129, 0.3); +} + +.action-btn-new.danger { + @apply bg-red-600 hover:bg-red-700 text-white dark:bg-red-600 dark:hover:bg-red-500; + box-shadow: 0 5px 15px rgba(239, 68, 68, 0.2); +} + +.dark .action-btn-new.danger { + box-shadow: 0 5px 15px rgba(239, 68, 68, 0.3); +} + +/* Informationszeilen in Drucker-Karten */ +.printer-info-row { + @apply flex items-center gap-2 text-xs sm:text-sm text-slate-700 dark:text-slate-300 mb-1.5; +} + +.printer-info-icon { + @apply w-3.5 h-3.5 sm:w-4 sm:h-4 text-slate-500 dark:text-slate-400 flex-shrink-0; +} + +/* Online-Indikator mit Pulseffekt */ +.online-indicator { + @apply absolute top-2.5 right-2.5 w-3 h-3 bg-green-500 rounded-full shadow-lg; + box-shadow: 0 0 0 rgba(16, 185, 129, 0.6); + animation: pulse-ring 2s cubic-bezier(0.455, 0.03, 0.515, 0.955) infinite; +} + +@keyframes pulse-ring { + 0% { + box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.6); + } + 70% { + box-shadow: 0 0 0 6px rgba(16, 185, 129, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); + } +} + +/* Header mit verbesserten Status-Anzeigen */ +.status-overview-new { + @apply flex flex-wrap gap-3 text-xs sm:text-sm p-3 bg-white/60 dark:bg-slate-800/60 backdrop-blur-xl rounded-lg border border-gray-200/60 dark:border-slate-700/30 shadow-lg; + box-shadow: + 0 10px 25px rgba(0, 0, 0, 0.04), + 0 5px 10px rgba(0, 0, 0, 0.02), 0 0 0 1px rgba(255, 255, 255, 0.1); } -.printer-card-new.online:hover { +.dark .status-overview-new { box-shadow: - 0 30px 60px rgba(34, 197, 94, 0.2), - 0 15px 30px rgba(34, 197, 94, 0.15), - 0 0 0 1px rgba(34, 197, 94, 0.3); + 0 10px 25px rgba(0, 0, 0, 0.15), + 0 5px 10px rgba(0, 0, 0, 0.1), + 0 0 0 1px rgba(255, 255, 255, 0.03); } -.dark .printer-card-new.online:hover { - box-shadow: - 0 30px 60px rgba(34, 197, 94, 0.4), - 0 15px 30px rgba(34, 197, 94, 0.3), - 0 0 0 1px rgba(34, 197, 94, 0.2); +.status-dot { + @apply w-2.5 h-2.5 rounded-full; } -.printer-card-new.offline:hover { - box-shadow: - 0 30px 60px rgba(239, 68, 68, 0.2), - 0 15px 30px rgba(239, 68, 68, 0.15), - 0 0 0 1px rgba(239, 68, 68, 0.3); +.status-dot.online { + @apply bg-green-500; + animation: pulse-dot 2s cubic-bezier(0.455, 0.03, 0.515, 0.955) infinite; } -.dark .printer-card-new.offline:hover { - box-shadow: - 0 30px 60px rgba(239, 68, 68, 0.4), - 0 15px 30px rgba(239, 68, 68, 0.3), - 0 0 0 1px rgba(239, 68, 68, 0.2); +.status-dot.offline { + @apply bg-red-500; } -/* Status-Indikatoren für Drucker */ -.printer-status-indicator { - @apply relative w-3 h-3 rounded-full; -} - -.printer-status-indicator.online { - @apply bg-green-500 shadow-lg; - box-shadow: 0 0 10px rgba(34, 197, 94, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.3); -} - -.printer-status-indicator.offline { - @apply bg-red-500 shadow-lg; - box-shadow: 0 0 10px rgba(239, 68, 68, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.3); -} - -.printer-status-indicator.warning { - @apply bg-yellow-500 shadow-lg; - box-shadow: 0 0 10px rgba(234, 179, 8, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.3); -} - -/* Pulsierende Animation für Online-Status */ -.printer-status-indicator.online::before { - content: ''; - position: absolute; - top: -2px; - left: -2px; - right: -2px; - bottom: -2px; - background: rgba(34, 197, 94, 0.3); - border-radius: 50%; - animation: pulse 2s infinite; -} - -@keyframes pulse { +@keyframes pulse-dot { 0% { - transform: scale(1); + transform: scale(0.95); opacity: 1; } + 50% { + transform: scale(1.1); + opacity: 0.8; + } 100% { - transform: scale(1.5); + transform: scale(0.95); + opacity: 1; + } +} + +/* Verbesserte Modals mit Glassmorphism */ +.modal-new { + @apply fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm; +} + +.modal-content-new { + @apply bg-white/90 dark:bg-slate-800/90 backdrop-blur-2xl rounded-2xl p-6 w-full max-w-md shadow-2xl border border-gray-200/60 dark:border-slate-700/30 transform transition-all duration-300; + box-shadow: + 0 25px 50px rgba(0, 0, 0, 0.15), + 0 15px 30px rgba(0, 0, 0, 0.1), + 0 0 0 1px rgba(255, 255, 255, 0.1); + animation: modal-in 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.dark .modal-content-new { + box-shadow: + 0 25px 50px rgba(0, 0, 0, 0.4), + 0 15px 30px rgba(0, 0, 0, 0.3), + 0 0 0 1px rgba(255, 255, 255, 0.05); +} + +@keyframes modal-in { + 0% { opacity: 0; + transform: scale(0.95) translateY(10px); + } + 100% { + opacity: 1; + transform: scale(1) translateY(0); } } -/* Drucker-Info-Container */ -.printer-info { - @apply space-y-2; +/* Verbesserte Input-Felder für Modals */ +.input-new { + @apply w-full px-3 py-2.5 bg-white/70 dark:bg-slate-700/70 border border-gray-300/60 dark:border-slate-600/60 rounded-lg text-slate-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-black dark:focus:ring-white focus:border-transparent transition-all duration-200 backdrop-blur-lg; + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); } -.printer-info-row { - @apply flex justify-between items-center text-sm; +.dark .input-new { + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); } -.printer-info-label { - @apply text-gray-600 dark:text-gray-400 font-medium; +/* Verbesserte Form-Labels */ +.form-label-new { + @apply block mb-2 text-sm font-medium text-slate-900 dark:text-white; } -.printer-info-value { - @apply text-gray-900 dark:text-gray-100 font-semibold; +/* Dark Mode Toggle - Premium Design */ +.dark-mode-toggle-premium { + @apply relative cursor-pointer outline-none focus:outline-none; + z-index: 100; /* Stellt sicher, dass der Button über anderen Elementen liegt */ } -/* Responsive Anpassungen für Drucker-Karten */ -@media (max-width: 768px) { - .printer-card-new { - @apply p-4; - } - - .printer-card-new:hover { - transform: translateY(-2px); - } +.dark-mode-toggle-premium:focus-visible { + @apply ring-2 ring-blue-500 ring-offset-2 dark:ring-blue-400 dark:ring-offset-slate-900 rounded-full; } -/* Mercedes-Schwarz Design für Jobs/Calendar/Request Seiten */ -.mercedes-page-container { - @apply min-h-screen transition-all duration-300; - background: linear-gradient(135deg, rgba(240, 249, 255, 0.8) 0%, rgba(219, 234, 254, 0.8) 100%); +/* Animationen für verzögerte Pulse-Effekte */ +@keyframes delayed-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } } -.dark .mercedes-page-container { - background: linear-gradient(135deg, rgba(0, 0, 0, 0.95) 0%, rgba(15, 23, 42, 0.95) 100%); +.animation-delay-500 { + animation-delay: 0.5s; + animation: delayed-pulse 2s ease-in-out infinite; } -/* Premium Card Design für alle Seiten */ -.mercedes-premium-card { - @apply backdrop-blur-2xl border rounded-3xl shadow-2xl transition-all duration-500 hover:shadow-3xl; - background: rgba(255, 255, 255, 0.85); - border: 1px solid rgba(255, 255, 255, 0.2); +.animation-delay-1000 { + animation-delay: 1s; + animation: delayed-pulse 2s ease-in-out infinite; +} + +.animation-delay-1500 { + animation-delay: 1.5s; + animation: delayed-pulse 2s ease-in-out infinite; +} + +/* Improved icon transitions */ +.dark-mode-toggle-premium .sun-icon, +.dark-mode-toggle-premium .moon-icon { + transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* Glassmorphism effect for better visual depth */ +.dark-mode-toggle-premium .backdrop-blur-md { + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); +} + +/* Enhanced hover effects */ +.dark-mode-toggle-premium:hover .sun-icon svg, +.dark-mode-toggle-premium:hover .moon-icon svg { + filter: drop-shadow(0 0 8px currentColor); + transform: scale(1.1); +} + +/* Smooth gradient transitions */ +.dark-mode-toggle-premium .bg-gradient-to-r { + transition: opacity 0.5s ease-in-out; +} + +/* Better shadow effects */ +.dark-mode-toggle-premium .shadow-lg { box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.1), - 0 0 0 1px rgba(255, 255, 255, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.4); - padding: 2rem; - margin: 1.5rem; + 0 10px 15px -3px rgba(0, 0, 0, 0.1), + 0 4px 6px -2px rgba(0, 0, 0, 0.05); } -.dark .mercedes-premium-card { - background: rgba(0, 0, 0, 0.8); - border: 1px solid rgba(255, 255, 255, 0.05); +.dark .dark-mode-toggle-premium .shadow-lg { box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.4), - 0 0 0 1px rgba(255, 255, 255, 0.03), - inset 0 1px 0 rgba(255, 255, 255, 0.1); + 0 10px 15px -3px rgba(0, 0, 0, 0.3), + 0 4px 6px -2px rgba(0, 0, 0, 0.2); } -/* Header Cards für Jobs/Calendar/Request */ -.mercedes-page-header { - @apply relative overflow-hidden rounded-3xl transition-all duration-500; - background: linear-gradient(135deg, #000000 0%, #1a1a1a 25%, #333333 50%, #4a4a4a 75%, #666666 100%); - padding: 3rem 2rem; - margin: 1.5rem; - color: white; +/* Rotation animation for smooth icon transitions */ +@keyframes icon-rotate-in { + 0% { + opacity: 0; + transform: rotate(-90deg) scale(0.8); + } + 100% { + opacity: 1; + transform: rotate(0deg) scale(1); + } +} + +.dark-mode-toggle-premium .sun-icon.icon-enter, +.dark-mode-toggle-premium .moon-icon.icon-enter { + animation: icon-rotate-in 0.5s ease-out forwards; +} + +/* Active state animation */ +.dark-mode-toggle-premium:active > div { + transform: scale(0.95); + transition: transform 0.1s ease-out; +} + +/* User Menu Button - Neues Design */ +.user-menu-button-new { + @apply flex items-center space-x-2 rounded-lg p-1.5 transition-all duration-300; + background: rgba(241, 245, 249, 0.6); + border: 1px solid rgba(255, 255, 255, 0.6); + box-shadow: + 0 2px 10px rgba(0, 0, 0, 0.04), + 0 1px 2px rgba(0, 0, 0, 0.02); +} + +.user-menu-button-new:hover { + @apply transform -translate-y-0.5; + background: rgba(241, 245, 249, 0.8); + box-shadow: + 0 10px 20px rgba(0, 0, 0, 0.06), + 0 2px 6px rgba(0, 0, 0, 0.04); +} + +.dark .user-menu-button-new { + background: rgba(30, 41, 59, 0.6); + border: 1px solid rgba(255, 255, 255, 0.08); + box-shadow: + 0 2px 10px rgba(0, 0, 0, 0.15), + 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.dark .user-menu-button-new:hover { + background: rgba(30, 41, 59, 0.8); + box-shadow: + 0 10px 20px rgba(0, 0, 0, 0.15), + 0 2px 6px rgba(0, 0, 0, 0.1); +} + +/* User Avatar - Neues Design */ +.user-avatar-new { + @apply h-8 w-8 rounded-full flex items-center justify-center text-white font-semibold text-sm shadow-md transition-all duration-300; + background: linear-gradient(135deg, #000000, #333333); + box-shadow: + 0 2px 6px rgba(0, 0, 0, 0.2), + 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.dark .user-avatar-new { + background: linear-gradient(135deg, #f8fafc, #e2e8f0); + color: #0f172a; + box-shadow: + 0 2px 6px rgba(0, 0, 0, 0.3), + 0 1px 3px rgba(0, 0, 0, 0.2); +} + +/* Login Button - Neues Design */ +.login-button-new { + @apply flex items-center px-4 py-2 rounded-lg text-sm font-medium shadow-sm transition-all duration-300; + background: #000000; + color: #ffffff; border: 1px solid rgba(255, 255, 255, 0.1); box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.1); + 0 2px 10px rgba(0, 0, 0, 0.1), + 0 1px 2px rgba(0, 0, 0, 0.08); } -.mercedes-page-header::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 80' width='80' height='80' opacity='0.03' fill='currentColor'%3E%3Cpath d='M58.6,4.5C53,1.6,46.7,0,40,0c-6.7,0-13,1.6-18.6,4.5v0C8.7,11.2,0,24.6,0,40c0,15.4,8.7,28.8,21.5,35.5C27,78.3,33.3,80,40,80c6.7,0,12.9-1.7,18.5-4.6C71.3,68.8,80,55.4,80,40C80,24.6,71.3,11.2,58.6,4.5z M4,40c0-13.1,7-24.5,17.5-30.9v0C26.6,6,32.5,4.2,39,4l-4.5,32.7L21.5,46.8v0L8.3,57.1C5.6,52,4,46.2,4,40z M58.6,70.8C53.1,74.1,46.8,76,40,76c-6.8,0-13.2-1.9-18.6-5.2c-4.9-2.9-8.9-6.9-11.9-11.7l11.9-4.9v0L40,46.6l18.6,7.5v0l12,4.9C67.6,63.9,63.4,67.9,58.6,70.8z M58.6,46.8L58.6,46.8l-12.9-10L41.1,4c6.3,0.2,12.3,2,17.4,5.1v0C69,15.4,76,26.9,76,40c0,6.2-1.5,12-4.3,17.1L58.6,46.8z'/%3E%3C/svg%3E"); - background-position: center; - background-repeat: repeat; - background-size: 40px 40px; - z-index: 0; -} - -.mercedes-page-header > * { - position: relative; - z-index: 1; -} - -/* Content Container für bessere Strukturierung */ -.mercedes-content-container { - @apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8; - padding-top: 2rem; - padding-bottom: 4rem; -} - -/* Form Elements mit Mercedes Design */ -.mercedes-form-input { - @apply w-full rounded-2xl border transition-all duration-300 font-medium; - padding: 1rem 1.5rem; - background: rgba(255, 255, 255, 0.9); - border: 1px solid rgba(229, 231, 235, 0.8); - color: #0f172a; - backdrop-filter: blur(10px); - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); -} - -.mercedes-form-input:focus { - @apply ring-4 ring-blue-500/25 border-blue-500; - transform: translateY(-2px); - box-shadow: 0 10px 25px -5px rgba(59, 130, 246, 0.3); -} - -.dark .mercedes-form-input { - background: rgba(30, 41, 59, 0.9); - border: 1px solid rgba(71, 85, 105, 0.8); - color: #f8fafc; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3); -} - -/* Premium Buttons */ -.mercedes-btn-primary { - @apply inline-flex items-center justify-center px-8 py-4 rounded-2xl font-semibold transition-all duration-300 transform hover:scale-105; - background: linear-gradient(135deg, #000000 0%, #333333 100%); - color: white; - border: 1px solid rgba(255, 255, 255, 0.1); +.login-button-new:hover { + @apply transform -translate-y-0.5; + background: #333333; box-shadow: - 0 10px 20px rgba(0, 0, 0, 0.2), - inset 0 1px 0 rgba(255, 255, 255, 0.1); + 0 10px 20px rgba(0, 0, 0, 0.15), + 0 3px 6px rgba(0, 0, 0, 0.1); } -.mercedes-btn-primary:hover { - background: linear-gradient(135deg, #1a1a1a 0%, #4a4a4a 100%); - box-shadow: - 0 15px 35px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.2); -} - -.dark .mercedes-btn-primary { - background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); - color: #0f172a; +.dark .login-button-new { + background: #ffffff; + color: #000000; border: 1px solid rgba(0, 0, 0, 0.1); -} - -.dark .mercedes-btn-primary:hover { - background: linear-gradient(135deg, #e2e8f0 0%, #cbd5e1 100%); box-shadow: - 0 15px 35px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.3); + 0 2px 10px rgba(0, 0, 0, 0.2), + 0 1px 2px rgba(0, 0, 0, 0.15); } -.mercedes-btn-secondary { - @apply inline-flex items-center justify-center px-6 py-3 rounded-xl font-medium transition-all duration-300; - background: rgba(255, 255, 255, 0.9); - color: #0f172a; - border: 1px solid rgba(229, 231, 235, 0.8); - backdrop-filter: blur(10px); - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); +.dark .login-button-new:hover { + background: #f1f5f9; + box-shadow: + 0 10px 20px rgba(0, 0, 0, 0.25), + 0 3px 6px rgba(0, 0, 0, 0.2); } -.mercedes-btn-secondary:hover { - background: rgba(255, 255, 255, 1); - transform: translateY(-2px); - box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.15); +/* Mobile Menu - Neues Design */ +.mobile-menu-new { + @apply w-full overflow-hidden transition-all duration-300 z-40; + background: rgba(255, 255, 255, 0.8); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06); + border-bottom: 1px solid rgba(241, 245, 249, 0.8); + max-height: 0; + opacity: 0; } -.dark .mercedes-btn-secondary { - background: rgba(30, 41, 59, 0.9); - color: #f8fafc; - border: 1px solid rgba(71, 85, 105, 0.8); - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3); +.mobile-menu-new.open { + max-height: 500px; + opacity: 1; + border-bottom: 1px solid rgba(241, 245, 249, 0.8); } -.dark .mercedes-btn-secondary:hover { - background: rgba(30, 41, 59, 1); - box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.4); -} - -/* Grid System für bessere Layout-Strukturierung */ -.mercedes-grid { - @apply grid gap-6; -} - -.mercedes-grid-1 { @apply grid-cols-1; } -.mercedes-grid-2 { @apply grid-cols-1 lg:grid-cols-2; } -.mercedes-grid-3 { @apply grid-cols-1 md:grid-cols-2 lg:grid-cols-3; } -.mercedes-grid-4 { @apply grid-cols-1 md:grid-cols-2 lg:grid-cols-4; } - -/* Spacing Utilities */ -.mercedes-spacing-sm { padding: 1rem; margin: 0.75rem; } -.mercedes-spacing-md { padding: 1.5rem; margin: 1rem; } -.mercedes-spacing-lg { padding: 2rem; margin: 1.5rem; } -.mercedes-spacing-xl { padding: 3rem; margin: 2rem; } - -/* Status Indicators */ -.mercedes-status-indicator { - @apply inline-flex items-center px-3 py-1.5 rounded-full text-sm font-medium; -} - -.mercedes-status-online { - @apply bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300; -} - -.mercedes-status-offline { - @apply bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300; -} - -.mercedes-status-pending { - @apply bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300; -} - -/* Table Styling */ -.mercedes-table { - @apply w-full rounded-2xl overflow-hidden; - background: rgba(255, 255, 255, 0.9); - backdrop-filter: blur(20px); - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.1); -} - -.dark .mercedes-table { - background: rgba(0, 0, 0, 0.8); - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.4); -} - -.mercedes-table th { - @apply px-6 py-4 text-left text-xs font-semibold uppercase tracking-wider; - background: rgba(248, 250, 252, 0.8); - color: #475569; - border-bottom: 1px solid rgba(226, 232, 240, 0.5); -} - -.dark .mercedes-table th { +.dark .mobile-menu-new { background: rgba(15, 23, 42, 0.8); - color: #94a3b8; - border-bottom: 1px solid rgba(51, 65, 85, 0.5); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); + border-bottom: 1px solid rgba(30, 41, 59, 0.8); } -.mercedes-table td { - @apply px-6 py-4 text-sm; +.mobile-nav-item { + @apply flex items-center space-x-3 px-4 py-3 rounded-lg text-slate-800 dark:text-slate-200 transition-all duration-300; +} + +.mobile-nav-item:hover { + background: rgba(241, 245, 249, 0.8); +} + +.dark .mobile-nav-item:hover { + background: rgba(30, 41, 59, 0.6); +} + +.mobile-nav-item.active { + background: rgba(241, 245, 249, 0.9); + color: #000000; + font-weight: 500; +} + +.dark .mobile-nav-item.active { + background: rgba(30, 41, 59, 0.8); + color: #ffffff; +} + +/* Dashboard Stat Cards mit schwarzem Hintergrund im Dark Mode */ +.mb-stat-card { + 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, 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/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 - Verstärktes Glassmorphism */ +footer { + @apply transition-all duration-300; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(30px) saturate(180%) brightness(120%); + -webkit-backdrop-filter: blur(30px) saturate(180%) brightness(120%); + border-top: 1px solid rgba(255, 255, 255, 0.2); + box-shadow: + 0 -8px 32px rgba(0, 0, 0, 0.1), + 0 -2px 8px rgba(0, 0, 0, 0.05), + inset 0 1px 0 rgba(255, 255, 255, 0.2), + 0 0 0 1px rgba(255, 255, 255, 0.05); +} + +.dark footer { + background: rgba(0, 0, 0, 0.3); + backdrop-filter: blur(30px) saturate(160%) brightness(110%); + -webkit-backdrop-filter: blur(30px) saturate(160%) brightness(110%); border-bottom: 1px solid rgba(226, 232, 240, 0.3); }