🎉 Fix for JOBS_UNDEFINED and LOG_EXPORT issues, updated documentation 📚 in backend/docs.

This commit is contained in:
Till Tomczak 2025-06-01 04:04:34 +02:00
parent 45d8d46556
commit 5ee854cbc6
43 changed files with 3053 additions and 118 deletions

View File

@ -5842,6 +5842,84 @@ def api_logs():
'error': f'Fehler beim Abrufen der Log-Daten: {str(e)}' 'error': f'Fehler beim Abrufen der Log-Daten: {str(e)}'
}), 500 }), 500
@app.route('/api/admin/logs/export', methods=['GET'])
@login_required
@admin_required
def export_admin_logs():
"""
Exportiert System-Logs als ZIP-Datei
Sammelt alle verfügbaren Log-Dateien und komprimiert sie in eine herunterladbare ZIP-Datei
"""
try:
import os
import zipfile
import tempfile
from datetime import datetime
# Temporäre ZIP-Datei erstellen
temp_dir = tempfile.mkdtemp()
zip_filename = f"myp_logs_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip"
zip_path = os.path.join(temp_dir, zip_filename)
log_dir = os.path.join(os.path.dirname(__file__), 'logs')
# Prüfen ob Log-Verzeichnis existiert
if not os.path.exists(log_dir):
app_logger.warning(f"Log-Verzeichnis nicht gefunden: {log_dir}")
return jsonify({
"success": False,
"message": "Log-Verzeichnis nicht gefunden"
}), 404
# ZIP-Datei erstellen und Log-Dateien hinzufügen
files_added = 0
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for root, dirs, files in os.walk(log_dir):
for file in files:
if file.endswith('.log'):
file_path = os.path.join(root, file)
try:
# Relativen Pfad für Archiv erstellen
arcname = os.path.relpath(file_path, log_dir)
zipf.write(file_path, arcname)
files_added += 1
app_logger.debug(f"Log-Datei hinzugefügt: {arcname}")
except Exception as file_error:
app_logger.warning(f"Fehler beim Hinzufügen der Datei {file_path}: {str(file_error)}")
continue
# Prüfen ob Dateien hinzugefügt wurden
if files_added == 0:
# Leere ZIP-Datei löschen
try:
os.remove(zip_path)
os.rmdir(temp_dir)
except:
pass
return jsonify({
"success": False,
"message": "Keine Log-Dateien zum Exportieren gefunden"
}), 404
app_logger.info(f"System-Logs exportiert: {files_added} Dateien in {zip_filename}")
# ZIP-Datei als Download senden
return send_file(
zip_path,
as_attachment=True,
download_name=zip_filename,
mimetype='application/zip'
)
except Exception as e:
app_logger.error(f"Fehler beim Exportieren der Logs: {str(e)}")
return jsonify({
"success": False,
"message": f"Fehler beim Exportieren: {str(e)}"
}), 500
# ===== FEHLENDE ADMIN API-ENDPUNKTE ===== # ===== FEHLENDE ADMIN API-ENDPUNKTE =====
@app.route("/api/admin/database/status", methods=['GET']) @app.route("/api/admin/database/status", methods=['GET'])

Binary file not shown.

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

View File

@ -9,3 +9,7 @@
2025-06-01 03:30:47 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert 2025-06-01 03:30:47 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert
2025-06-01 03:49:33 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert 2025-06-01 03:49:33 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert
2025-06-01 03:50:54 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert 2025-06-01 03:50:54 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert
2025-06-01 03:39:15 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert
2025-06-01 03:42:25 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert
2025-06-01 03:42:33 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert
2025-06-01 04:01:30 - [analytics] analytics - [INFO] INFO - 📈 Analytics Engine initialisiert

View File

@ -351,3 +351,48 @@ WHERE users.id = ?
2025-06-01 03:51:05 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_stats_live: User authenticated: True, User ID: 1, Is Admin: True 2025-06-01 03:51:05 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_stats_live: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 03:51:06 - [app] app - [WARNING] WARNING - System-Performance-Metriken nicht verfügbar: argument 1 (impossible<bad format char>) 2025-06-01 03:51:06 - [app] app - [WARNING] WARNING - System-Performance-Metriken nicht verfügbar: argument 1 (impossible<bad format char>)
2025-06-01 03:51:06 - [app] app - [INFO] INFO - Admin-Check für Funktion api_logs: User authenticated: True, User ID: 1, Is Admin: True 2025-06-01 03:51:06 - [app] app - [INFO] INFO - Admin-Check für Funktion api_logs: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 03:39:14 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: /mnt/database/myp.db
2025-06-01 03:39:15 - [app] app - [INFO] INFO - SQLite für Produktionsumgebung konfiguriert (WAL-Modus, Cache, Optimierungen)
2025-06-01 03:42:25 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: /mnt/database/myp.db
2025-06-01 03:42:26 - [app] app - [INFO] INFO - SQLite für Produktionsumgebung konfiguriert (WAL-Modus, Cache, Optimierungen)
2025-06-01 03:42:26 - [app] app - [INFO] INFO - ✅ Timeout Force-Quit Manager geladen
2025-06-01 03:42:26 - [app] app - [INFO] INFO - ✅ Zentraler Shutdown-Manager initialisiert
2025-06-01 03:42:26 - [app] app - [INFO] INFO - 🔄 Starte Datenbank-Setup und Migrationen...
2025-06-01 03:42:26 - [app] app - [INFO] INFO - Datenbank mit Optimierungen initialisiert
2025-06-01 03:42:26 - [app] app - [INFO] INFO - ✅ JobOrder-Tabelle bereits vorhanden
2025-06-01 03:42:26 - [app] app - [INFO] INFO - Admin-Benutzer admin (admin@mercedes-benz.com) existiert bereits. Passwort wurde zurückgesetzt.
2025-06-01 03:42:26 - [app] app - [INFO] INFO - ✅ Datenbank-Setup und Migrationen erfolgreich abgeschlossen
2025-06-01 03:42:26 - [app] app - [INFO] INFO - 🖨️ Starte automatische Steckdosen-Initialisierung...
2025-06-01 03:42:26 - [app] app - [INFO] INFO - Keine Drucker zur Initialisierung gefunden
2025-06-01 03:42:26 - [app] app - [INFO] INFO - ✅ Printer Queue Manager erfolgreich gestartet
2025-06-01 03:42:27 - [app] app - [INFO] INFO - Job-Scheduler gestartet
2025-06-01 03:42:27 - [app] app - [INFO] INFO - Starte HTTPS-Server auf 0.0.0.0:443
2025-06-01 03:42:33 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: /mnt/database/myp.db
2025-06-01 03:42:34 - [app] app - [INFO] INFO - SQLite für Produktionsumgebung konfiguriert (WAL-Modus, Cache, Optimierungen)
2025-06-01 03:42:34 - [app] app - [INFO] INFO - ✅ Timeout Force-Quit Manager geladen
2025-06-01 03:42:34 - [app] app - [INFO] INFO - ✅ Zentraler Shutdown-Manager initialisiert
2025-06-01 03:42:34 - [app] app - [INFO] INFO - 🔄 Starte Datenbank-Setup und Migrationen...
2025-06-01 03:42:34 - [app] app - [INFO] INFO - Datenbank mit Optimierungen initialisiert
2025-06-01 03:42:34 - [app] app - [INFO] INFO - ✅ JobOrder-Tabelle bereits vorhanden
2025-06-01 03:42:34 - [app] app - [INFO] INFO - Admin-Benutzer admin (admin@mercedes-benz.com) existiert bereits. Passwort wurde zurückgesetzt.
2025-06-01 03:42:34 - [app] app - [INFO] INFO - ✅ Datenbank-Setup und Migrationen erfolgreich abgeschlossen
2025-06-01 03:42:34 - [app] app - [INFO] INFO - 🖨️ Starte automatische Steckdosen-Initialisierung...
2025-06-01 03:42:34 - [app] app - [INFO] INFO - Keine Drucker zur Initialisierung gefunden
2025-06-01 03:42:34 - [app] app - [INFO] INFO - ✅ Printer Queue Manager erfolgreich gestartet
2025-06-01 03:42:34 - [app] app - [INFO] INFO - Job-Scheduler gestartet
2025-06-01 03:42:34 - [app] app - [INFO] INFO - Starte HTTPS-Server auf 0.0.0.0:443
2025-06-01 04:01:29 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db
2025-06-01 04:01:31 - [app] app - [INFO] INFO - SQLite für Produktionsumgebung konfiguriert (WAL-Modus, Cache, Optimierungen)
2025-06-01 04:01:31 - [app] app - [INFO] INFO - ✅ Timeout Force-Quit Manager geladen
2025-06-01 04:01:31 - [app] app - [INFO] INFO - ✅ Zentraler Shutdown-Manager initialisiert
2025-06-01 04:01:31 - [app] app - [INFO] INFO - 🔄 Starte Datenbank-Setup und Migrationen...
2025-06-01 04:01:31 - [app] app - [INFO] INFO - Datenbank mit Optimierungen initialisiert
2025-06-01 04:01:31 - [app] app - [INFO] INFO - ✅ JobOrder-Tabelle bereits vorhanden
2025-06-01 04:01:31 - [app] app - [INFO] INFO - Admin-Benutzer admin (admin@mercedes-benz.com) existiert bereits. Passwort wurde zurückgesetzt.
2025-06-01 04:01:31 - [app] app - [INFO] INFO - ✅ Datenbank-Setup und Migrationen erfolgreich abgeschlossen
2025-06-01 04:01:31 - [app] app - [INFO] INFO - 🖨️ Starte automatische Steckdosen-Initialisierung...
2025-06-01 04:01:31 - [app] app - [INFO] INFO - Keine Drucker zur Initialisierung gefunden
2025-06-01 04:01:31 - [app] app - [INFO] INFO - 🔄 Debug-Modus: Queue Manager deaktiviert für Entwicklung
2025-06-01 04:01:31 - [app] app - [INFO] INFO - Job-Scheduler gestartet
2025-06-01 04:01:31 - [app] app - [INFO] INFO - Starte Debug-Server auf 0.0.0.0:5000 (HTTP)
2025-06-01 04:01:31 - [app] app - [INFO] INFO - Windows-Debug-Modus: Auto-Reload deaktiviert

View File

@ -9,3 +9,7 @@
2025-06-01 03:30:47 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation) 2025-06-01 03:30:47 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation)
2025-06-01 03:49:33 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation) 2025-06-01 03:49:33 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation)
2025-06-01 03:50:54 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation) 2025-06-01 03:50:54 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation)
2025-06-01 03:39:15 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation)
2025-06-01 03:42:25 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation)
2025-06-01 03:42:33 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation)
2025-06-01 04:01:29 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation)

View File

@ -4,3 +4,4 @@
2025-06-01 03:43:14 - [calendar] calendar - [INFO] INFO - 📅 Kalender-Events abgerufen: 0 Einträge für Zeitraum 2025-06-01 00:00:00 bis 2025-06-08 00:00:00 2025-06-01 03:43:14 - [calendar] calendar - [INFO] INFO - 📅 Kalender-Events abgerufen: 0 Einträge für Zeitraum 2025-06-01 00:00:00 bis 2025-06-08 00:00:00
2025-06-01 03:49:50 - [calendar] calendar - [INFO] INFO - 📅 Kalender-Events abgerufen: 0 Einträge für Zeitraum 2025-06-01 00:00:00 bis 2025-06-08 00:00:00 2025-06-01 03:49:50 - [calendar] calendar - [INFO] INFO - 📅 Kalender-Events abgerufen: 0 Einträge für Zeitraum 2025-06-01 00:00:00 bis 2025-06-08 00:00:00
2025-06-01 03:50:49 - [calendar] calendar - [INFO] INFO - 📅 Kalender-Events abgerufen: 0 Einträge für Zeitraum 2025-06-01 00:00:00 bis 2025-06-08 00:00:00 2025-06-01 03:50:49 - [calendar] calendar - [INFO] INFO - 📅 Kalender-Events abgerufen: 0 Einträge für Zeitraum 2025-06-01 00:00:00 bis 2025-06-08 00:00:00
2025-06-01 04:01:49 - [calendar] calendar - [INFO] INFO - 📅 Kalender-Events abgerufen: 0 Einträge für Zeitraum 2025-06-01 00:00:00 bis 2025-06-08 00:00:00

View File

@ -32,3 +32,16 @@
2025-06-01 03:50:55 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet 2025-06-01 03:50:55 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet
2025-06-01 03:50:55 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server wird mit threading initialisiert (eventlet-Fallback) 2025-06-01 03:50:55 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server wird mit threading initialisiert (eventlet-Fallback)
2025-06-01 03:50:55 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server initialisiert (async_mode: threading) 2025-06-01 03:50:55 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server initialisiert (async_mode: threading)
2025-06-01 03:39:15 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet
2025-06-01 03:42:26 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet
2025-06-01 03:42:26 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet
2025-06-01 03:42:26 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server wird mit threading initialisiert (eventlet-Fallback)
2025-06-01 03:42:26 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server initialisiert (async_mode: threading)
2025-06-01 03:42:34 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet
2025-06-01 03:42:34 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet
2025-06-01 03:42:34 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server wird mit threading initialisiert (eventlet-Fallback)
2025-06-01 03:42:34 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server initialisiert (async_mode: threading)
2025-06-01 04:01:31 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet
2025-06-01 04:01:31 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet
2025-06-01 04:01:31 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server wird mit threading initialisiert (eventlet-Fallback)
2025-06-01 04:01:31 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server initialisiert (async_mode: threading)

View File

@ -9,3 +9,7 @@
2025-06-01 03:30:47 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet 2025-06-01 03:30:47 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet
2025-06-01 03:49:33 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet 2025-06-01 03:49:33 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet
2025-06-01 03:50:54 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet 2025-06-01 03:50:54 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet
2025-06-01 03:39:15 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet
2025-06-01 03:42:25 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet
2025-06-01 03:42:33 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet
2025-06-01 04:01:29 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet

View File

@ -8,3 +8,6 @@
2025-06-01 03:43:03 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand) 2025-06-01 03:43:03 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
2025-06-01 03:49:34 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand) 2025-06-01 03:49:34 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
2025-06-01 03:50:55 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand) 2025-06-01 03:50:55 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
2025-06-01 03:42:26 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
2025-06-01 03:42:34 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)
2025-06-01 04:01:31 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand)

View File

@ -39,3 +39,4 @@
2025-06-01 03:49:46 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 0 von 0 (Seite 1) 2025-06-01 03:49:46 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 0 von 0 (Seite 1)
2025-06-01 03:50:44 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 0 von 0 (Seite 1) 2025-06-01 03:50:44 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 0 von 0 (Seite 1)
2025-06-01 03:50:53 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 0 von 0 (Seite 1) 2025-06-01 03:50:53 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 0 von 0 (Seite 1)
2025-06-01 04:01:40 - [jobs] jobs - [INFO] INFO - Jobs abgerufen: 0 von 0 (Seite 1)

View File

@ -16,3 +16,9 @@
2025-06-01 03:49:34 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet 2025-06-01 03:49:34 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
2025-06-01 03:50:55 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet 2025-06-01 03:50:55 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
2025-06-01 03:50:55 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet 2025-06-01 03:50:55 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
2025-06-01 03:42:26 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
2025-06-01 03:42:26 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
2025-06-01 03:42:34 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
2025-06-01 03:42:34 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
2025-06-01 04:01:31 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet
2025-06-01 04:01:31 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet

View File

@ -16,3 +16,9 @@
2025-06-01 03:49:34 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt 2025-06-01 03:49:34 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
2025-06-01 03:50:55 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt 2025-06-01 03:50:55 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
2025-06-01 03:50:55 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt 2025-06-01 03:50:55 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
2025-06-01 03:42:26 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
2025-06-01 03:42:26 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
2025-06-01 03:42:34 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
2025-06-01 03:42:34 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
2025-06-01 04:01:31 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt
2025-06-01 04:01:31 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt

View File

@ -6,3 +6,6 @@
2025-06-01 03:43:03 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert 2025-06-01 03:43:03 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert
2025-06-01 03:49:34 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert 2025-06-01 03:49:34 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert
2025-06-01 03:50:55 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert 2025-06-01 03:50:55 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert
2025-06-01 03:42:26 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert
2025-06-01 03:42:34 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert
2025-06-01 04:01:31 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert

View File

@ -319,3 +319,46 @@
2025-06-01 03:51:08 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 3/6: 192.168.0.100 2025-06-01 03:51:08 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 3/6: 192.168.0.100
2025-06-01 03:51:14 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 4/6: 192.168.0.101 2025-06-01 03:51:14 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 4/6: 192.168.0.101
2025-06-01 03:51:20 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 5/6: 192.168.0.102 2025-06-01 03:51:20 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 5/6: 192.168.0.102
2025-06-01 03:39:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🖨️ Drucker-Monitor initialisiert
2025-06-01 03:39:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
2025-06-01 03:42:25 - [printer_monitor] printer_monitor - [INFO] INFO - 🖨️ Drucker-Monitor initialisiert
2025-06-01 03:42:25 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
2025-06-01 03:42:26 - [printer_monitor] printer_monitor - [INFO] INFO - 🚀 Starte Steckdosen-Initialisierung beim Programmstart...
2025-06-01 03:42:26 - [printer_monitor] printer_monitor - [WARNING] WARNING - ⚠️ Keine aktiven Drucker zur Initialisierung gefunden
2025-06-01 03:42:27 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Starte automatische Tapo-Steckdosenerkennung...
2025-06-01 03:42:27 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Teste 6 Standard-IPs aus der Konfiguration
2025-06-01 03:42:27 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 1/6: 192.168.0.103
2025-06-01 03:42:33 - [printer_monitor] printer_monitor - [INFO] INFO - 🖨️ Drucker-Monitor initialisiert
2025-06-01 03:42:33 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
2025-06-01 03:42:34 - [printer_monitor] printer_monitor - [INFO] INFO - 🚀 Starte Steckdosen-Initialisierung beim Programmstart...
2025-06-01 03:42:34 - [printer_monitor] printer_monitor - [WARNING] WARNING - ⚠️ Keine aktiven Drucker zur Initialisierung gefunden
2025-06-01 03:42:35 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Starte automatische Tapo-Steckdosenerkennung...
2025-06-01 03:42:35 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Teste 6 Standard-IPs aus der Konfiguration
2025-06-01 03:42:35 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 1/6: 192.168.0.103
2025-06-01 03:42:44 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 2/6: 192.168.0.104
2025-06-01 03:42:51 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 3/6: 192.168.0.100
2025-06-01 03:42:59 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 4/6: 192.168.0.101
2025-06-01 03:43:06 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 5/6: 192.168.0.102
2025-06-01 03:43:13 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 6/6: 192.168.0.105
2025-06-01 03:43:20 - [printer_monitor] printer_monitor - [INFO] INFO - ✅ Steckdosen-Erkennung abgeschlossen: 0/6 Steckdosen gefunden in 44.2s
2025-06-01 04:01:29 - [printer_monitor] printer_monitor - [INFO] INFO - 🖨️ Drucker-Monitor initialisiert
2025-06-01 04:01:29 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet
2025-06-01 04:01:31 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Starte automatische Tapo-Steckdosenerkennung...
2025-06-01 04:01:31 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Teste 6 Standard-IPs aus der Konfiguration
2025-06-01 04:01:31 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 1/6: 192.168.0.103
2025-06-01 04:01:31 - [printer_monitor] printer_monitor - [INFO] INFO - 🚀 Starte Steckdosen-Initialisierung beim Programmstart...
2025-06-01 04:01:31 - [printer_monitor] printer_monitor - [WARNING] WARNING - ⚠️ Keine aktiven Drucker zur Initialisierung gefunden
2025-06-01 04:01:37 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 2/6: 192.168.0.104
2025-06-01 04:01:38 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 04:01:38 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 04:01:38 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 04:01:38 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 04:01:42 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 04:01:42 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 04:01:42 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 04:01:42 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 04:01:43 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 3/6: 192.168.0.100
2025-06-01 04:01:49 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 4/6: 192.168.0.101
2025-06-01 04:01:55 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 5/6: 192.168.0.102
2025-06-01 04:02:01 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 6/6: 192.168.0.105
2025-06-01 04:02:07 - [printer_monitor] printer_monitor - [INFO] INFO - ✅ Steckdosen-Erkennung abgeschlossen: 0/6 Steckdosen gefunden in 36.0s

View File

@ -3073,3 +3073,12 @@
2025-06-01 03:51:04 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) 2025-06-01 03:51:04 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 03:51:04 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker 2025-06-01 03:51:04 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 03:51:04 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 11.94ms 2025-06-01 03:51:04 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 11.94ms
2025-06-01 04:01:38 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 04:01:38 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 04:01:38 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 6.07ms
2025-06-01 04:01:40 - [printers] printers - [INFO] INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)
2025-06-01 04:01:42 - [printers] printers - [INFO] INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)
2025-06-01 04:01:42 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 04:01:42 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 04:01:42 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 6.22ms
2025-06-01 04:01:42 - [printers] printers - [INFO] INFO - Schnelles Laden abgeschlossen: 6 Drucker geladen (ohne Status-Check)

View File

@ -20,3 +20,25 @@
2025-06-01 03:51:23 - [queue_manager] queue_manager - [INFO] INFO - 🛑 Shutdown-Signal empfangen - beende Monitor-Loop 2025-06-01 03:51:23 - [queue_manager] queue_manager - [INFO] INFO - 🛑 Shutdown-Signal empfangen - beende Monitor-Loop
2025-06-01 03:51:23 - [queue_manager] queue_manager - [INFO] INFO - 🔚 Monitor-Loop beendet 2025-06-01 03:51:23 - [queue_manager] queue_manager - [INFO] INFO - 🔚 Monitor-Loop beendet
2025-06-01 03:51:23 - [queue_manager] queue_manager - [INFO] INFO - ✅ Queue-Manager erfolgreich gestoppt 2025-06-01 03:51:23 - [queue_manager] queue_manager - [INFO] INFO - ✅ Queue-Manager erfolgreich gestoppt
2025-06-01 03:42:26 - [queue_manager] queue_manager - [INFO] INFO - 🚀 Initialisiere neuen Queue-Manager...
2025-06-01 03:42:26 - [queue_manager] queue_manager - [INFO] INFO - 🔄 Zentrale Shutdown-Verwaltung erkannt - deaktiviere lokale Signal-Handler
2025-06-01 03:42:26 - [queue_manager] queue_manager - [INFO] INFO - 🚀 Starte Printer Queue Manager...
2025-06-01 03:42:26 - [queue_manager] queue_manager - [INFO] INFO - 🔄 Queue-Überwachung gestartet (Intervall: 120 Sekunden)
2025-06-01 03:42:26 - [queue_manager] queue_manager - [INFO] INFO - ✅ Printer Queue Manager gestartet
2025-06-01 03:42:26 - [queue_manager] queue_manager - [INFO] INFO - ✅ Queue-Manager erfolgreich gestartet
2025-06-01 03:42:31 - [queue_manager] queue_manager - [INFO] INFO - 🔄 Stoppe Queue-Manager...
2025-06-01 03:42:31 - [queue_manager] queue_manager - [INFO] INFO - ⏳ Warte auf Monitor-Thread...
2025-06-01 03:42:31 - [queue_manager] queue_manager - [INFO] INFO - 🛑 Shutdown-Signal empfangen - beende Monitor-Loop
2025-06-01 03:42:31 - [queue_manager] queue_manager - [INFO] INFO - 🔚 Monitor-Loop beendet
2025-06-01 03:42:31 - [queue_manager] queue_manager - [INFO] INFO - ✅ Queue-Manager erfolgreich gestoppt
2025-06-01 03:42:34 - [queue_manager] queue_manager - [INFO] INFO - 🚀 Initialisiere neuen Queue-Manager...
2025-06-01 03:42:34 - [queue_manager] queue_manager - [INFO] INFO - 🔄 Zentrale Shutdown-Verwaltung erkannt - deaktiviere lokale Signal-Handler
2025-06-01 03:42:34 - [queue_manager] queue_manager - [INFO] INFO - 🚀 Starte Printer Queue Manager...
2025-06-01 03:42:34 - [queue_manager] queue_manager - [INFO] INFO - 🔄 Queue-Überwachung gestartet (Intervall: 120 Sekunden)
2025-06-01 03:42:34 - [queue_manager] queue_manager - [INFO] INFO - ✅ Printer Queue Manager gestartet
2025-06-01 03:42:34 - [queue_manager] queue_manager - [INFO] INFO - ✅ Queue-Manager erfolgreich gestartet
2025-06-01 03:43:52 - [queue_manager] queue_manager - [INFO] INFO - 🔄 Stoppe Queue-Manager...
2025-06-01 03:43:52 - [queue_manager] queue_manager - [INFO] INFO - ⏳ Warte auf Monitor-Thread...
2025-06-01 03:43:52 - [queue_manager] queue_manager - [INFO] INFO - 🛑 Shutdown-Signal empfangen - beende Monitor-Loop
2025-06-01 03:43:52 - [queue_manager] queue_manager - [INFO] INFO - 🔚 Monitor-Loop beendet
2025-06-01 03:43:52 - [queue_manager] queue_manager - [INFO] INFO - ✅ Queue-Manager erfolgreich gestoppt

View File

@ -2847,3 +2847,13 @@
2025-06-01 03:50:54 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True 2025-06-01 03:50:54 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 03:50:55 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet 2025-06-01 03:50:55 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
2025-06-01 03:50:55 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet 2025-06-01 03:50:55 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet
2025-06-01 03:39:15 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 03:42:25 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 03:42:27 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
2025-06-01 03:42:27 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet
2025-06-01 03:42:33 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 03:42:34 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
2025-06-01 03:42:34 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet
2025-06-01 04:01:29 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-01 04:01:31 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
2025-06-01 04:01:31 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet

View File

@ -6,3 +6,6 @@
2025-06-01 03:43:03 - [security] security - [INFO] INFO - 🔒 Security System initialisiert 2025-06-01 03:43:03 - [security] security - [INFO] INFO - 🔒 Security System initialisiert
2025-06-01 03:49:34 - [security] security - [INFO] INFO - 🔒 Security System initialisiert 2025-06-01 03:49:34 - [security] security - [INFO] INFO - 🔒 Security System initialisiert
2025-06-01 03:50:55 - [security] security - [INFO] INFO - 🔒 Security System initialisiert 2025-06-01 03:50:55 - [security] security - [INFO] INFO - 🔒 Security System initialisiert
2025-06-01 03:42:26 - [security] security - [INFO] INFO - 🔒 Security System initialisiert
2025-06-01 03:42:34 - [security] security - [INFO] INFO - 🔒 Security System initialisiert
2025-06-01 04:01:31 - [security] security - [INFO] INFO - 🔒 Security System initialisiert

View File

@ -39,3 +39,6 @@
2025-06-01 03:43:03 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert 2025-06-01 03:43:03 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert
2025-06-01 03:49:34 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert 2025-06-01 03:49:34 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert
2025-06-01 03:50:55 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert 2025-06-01 03:50:55 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert
2025-06-01 03:42:26 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert
2025-06-01 03:42:34 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert
2025-06-01 04:01:31 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert

View File

@ -70,3 +70,26 @@
2025-06-01 03:50:55 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert 2025-06-01 03:50:55 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert
2025-06-01 03:50:55 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert 2025-06-01 03:50:55 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert
2025-06-01 03:50:55 - [startup] startup - [INFO] INFO - ================================================== 2025-06-01 03:50:55 - [startup] startup - [INFO] INFO - ==================================================
2025-06-01 03:42:26 - [startup] startup - [INFO] INFO - ==================================================
2025-06-01 03:42:26 - [startup] startup - [INFO] INFO - 🚀 MYP Platform Backend wird gestartet...
2025-06-01 03:42:26 - [startup] startup - [INFO] INFO - 🐍 Python Version: 3.11.2 (main, Apr 28 2025, 14:11:48) [GCC 12.2.0]
2025-06-01 03:42:26 - [startup] startup - [INFO] INFO - 💻 Betriebssystem: posix (linux)
2025-06-01 03:42:26 - [startup] startup - [INFO] INFO - 📁 Arbeitsverzeichnis: /mnt
2025-06-01 03:42:26 - [startup] startup - [INFO] INFO - ⏰ Startzeit: 2025-06-01T03:42:26.267864
2025-06-01 03:42:26 - [startup] startup - [INFO] INFO - ==================================================
2025-06-01 03:42:34 - [startup] startup - [INFO] INFO - ==================================================
2025-06-01 03:42:34 - [startup] startup - [INFO] INFO - 🚀 MYP Platform Backend wird gestartet...
2025-06-01 03:42:34 - [startup] startup - [INFO] INFO - 🐍 Python Version: 3.11.2 (main, Apr 28 2025, 14:11:48) [GCC 12.2.0]
2025-06-01 03:42:34 - [startup] startup - [INFO] INFO - 💻 Betriebssystem: posix (linux)
2025-06-01 03:42:34 - [startup] startup - [INFO] INFO - 📁 Arbeitsverzeichnis: /mnt
2025-06-01 03:42:34 - [startup] startup - [INFO] INFO - ⏰ Startzeit: 2025-06-01T03:42:34.257182
2025-06-01 03:42:34 - [startup] startup - [INFO] INFO - ==================================================
2025-06-01 04:01:31 - [startup] startup - [INFO] INFO - ==================================================
2025-06-01 04:01:31 - [startup] startup - [INFO] INFO - 🚀 MYP Platform Backend wird gestartet...
2025-06-01 04:01:31 - [startup] startup - [INFO] INFO - 🐍 Python Version: 3.13.3 (tags/v3.13.3:6280bb5, Apr 8 2025, 14:47:33) [MSC v.1943 64 bit (AMD64)]
2025-06-01 04:01:31 - [startup] startup - [INFO] INFO - 💻 Betriebssystem: nt (win32)
2025-06-01 04:01:31 - [startup] startup - [INFO] INFO - 📁 Arbeitsverzeichnis: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend
2025-06-01 04:01:31 - [startup] startup - [INFO] INFO - ⏰ Startzeit: 2025-06-01T04:01:31.569736
2025-06-01 04:01:31 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert
2025-06-01 04:01:31 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert
2025-06-01 04:01:31 - [startup] startup - [INFO] INFO - ==================================================

View File

@ -46,3 +46,7 @@
2025-06-01 03:50:54 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen) 2025-06-01 03:50:54 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
2025-06-01 03:50:54 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Globaler subprocess-Patch angewendet 2025-06-01 03:50:54 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Globaler subprocess-Patch angewendet
2025-06-01 03:50:54 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet 2025-06-01 03:50:54 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
2025-06-01 04:01:29 - [windows_fixes] windows_fixes - [INFO] INFO - 🔧 Wende Windows-spezifische Fixes an...
2025-06-01 04:01:29 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen)
2025-06-01 04:01:29 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Globaler subprocess-Patch angewendet
2025-06-01 04:01:29 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet

View File

@ -1,9 +1,10 @@
# MYP Platform - Benötigte Python Dependencies # MYP Platform - Python Dependencies
# Nur tatsächlich verwendete Pakete # Aktualisiert: 2025-01-12
# Kompatibel mit Python 3.8+
# ===== CORE FRAMEWORK ===== # ===== CORE FRAMEWORK =====
Flask Flask>=2.3.0,<3.0.0
Werkzeug Werkzeug>=2.3.0,<3.0.0
# ===== FLASK EXTENSIONS ===== # ===== FLASK EXTENSIONS =====
Flask-Login Flask-Login
@ -13,10 +14,10 @@ WTForms
Flask-CORS Flask-CORS
# ===== DATABASE ===== # ===== DATABASE =====
SQLAlchemy SQLAlchemy>=2.0.0,<3.0.0
# ===== SECURITY ===== # ===== SECURITY =====
cryptography cryptography>=41.0.0
bcrypt bcrypt
PyJWT PyJWT
itsdangerous itsdangerous
@ -41,7 +42,7 @@ APScheduler
# ===== GIS & LOCATION ===== # ===== GIS & LOCATION =====
geocoder geocoder
# ===== DATA PROCESSING ===== # ===== DATA PROCESSING & EXPORT =====
openpyxl openpyxl
xlsxwriter xlsxwriter
pandas pandas
@ -49,37 +50,49 @@ chardet
python-magic python-magic
python-magic-bin; sys_platform == "win32" python-magic-bin; sys_platform == "win32"
# ===== EMAIL ===== # ===== EMAIL & VALIDATION =====
email-validator email-validator
# ===== IMAGE PROCESSING ===== # ===== IMAGE PROCESSING =====
Pillow Pillow
qrcode qrcode[pil]
# ===== PDF GENERATION ===== # ===== PDF & REPORT GENERATION =====
reportlab reportlab
weasyprint weasyprint
# ===== DATE/TIME ===== # ===== DATE/TIME HANDLING =====
python-dateutil python-dateutil
pytz pytz
# ===== LOGGING ===== # ===== LOGGING & MONITORING =====
colorlog colorlog
# ===== SYSTEM MONITORING =====
psutil psutil
# ===== FILE WATCHING ===== # ===== FILE SYSTEM OPERATIONS =====
watchdog watchdog
Send2Trash
# ===== VALIDATION ===== # ===== DATA VALIDATION =====
cerberus cerberus
marshmallow marshmallow
validators
# ===== UTILITIES ===== # ===== UTILITIES =====
python-slugify python-slugify
click click
humanize
python-dotenv
# ===== NETWORK & API =====
ping3
netifaces
# ===== CACHING =====
cachelib
# ===== COMPRESSION =====
py7zr
# ===== WINDOWS COMPATIBILITY ===== # ===== WINDOWS COMPATIBILITY =====
pywin32; sys_platform == "win32" pywin32; sys_platform == "win32"
@ -93,5 +106,20 @@ RPi.GPIO; sys_platform == "linux"
gunicorn; sys_platform != "win32" gunicorn; sys_platform != "win32"
waitress waitress
# ===== DEVELOPMENT ===== # ===== TESTING & DEVELOPMENT =====
python-dotenv pytest
pytest-flask
pytest-cov
coverage
# ===== CODE QUALITY =====
flake8
black
isort
# ===== OPTIONAL PERFORMANCE ENHANCEMENTS =====
# Uncomment for better performance:
# uwsgi; sys_platform != "win32"
# gevent
# redis
# celery

View File

@ -1055,48 +1055,792 @@
/* Benachrichtigungen */ /* Benachrichtigungen */
.notification { .notification {
@apply fixed top-4 right-4 max-w-md p-4 rounded-lg shadow-lg transform translate-x-full opacity-0 transition-all duration-300 z-50 bg-white dark:bg-slate-800 border-l-4; @apply fixed top-4 right-4 max-w-md p-4 rounded-2xl shadow-2xl transform translate-x-full opacity-0 transition-all duration-500 z-50;
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%);
-webkit-backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%);
border: 1px solid rgba(255, 255, 255, 0.25);
box-shadow:
0 32px 64px rgba(0, 0, 0, 0.25),
0 12px 24px rgba(0, 0, 0, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
0 0 0 1px rgba(255, 255, 255, 0.1);
animation: notification-slide-in 0.6s cubic-bezier(0.4, 0, 0.2, 1);
} }
.dark .notification {
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%);
-webkit-backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow:
0 32px 64px rgba(0, 0, 0, 0.6),
0 12px 24px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.2),
0 0 0 1px rgba(255, 255, 255, 0.05);
}
.notification.show { .notification.show {
@apply translate-x-0 opacity-100; @apply translate-x-0 opacity-100;
} }
.notification:hover {
transform: translateY(-2px) scale(1.02);
box-shadow:
0 40px 80px rgba(0, 0, 0, 0.3),
0 16px 32px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.5),
0 0 0 1px rgba(255, 255, 255, 0.15);
}
.dark .notification:hover {
box-shadow:
0 40px 80px rgba(0, 0, 0, 0.7),
0 16px 32px rgba(0, 0, 0, 0.5),
inset 0 1px 0 rgba(255, 255, 255, 0.3),
0 0 0 1px rgba(255, 255, 255, 0.1);
}
.notification-success { .notification-success {
@apply border-green-500; @apply text-green-100;
background: linear-gradient(135deg,
rgba(34, 197, 94, 0.25) 0%,
rgba(134, 239, 172, 0.18) 50%,
rgba(34, 197, 94, 0.12) 100%);
border: 1px solid rgba(34, 197, 94, 0.4);
box-shadow:
0 32px 64px rgba(34, 197, 94, 0.2),
0 12px 24px rgba(34, 197, 94, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
0 0 0 1px rgba(34, 197, 94, 0.3);
} }
.notification-error { .notification-error {
@apply border-red-500; @apply text-red-100;
background: linear-gradient(135deg,
rgba(239, 68, 68, 0.25) 0%,
rgba(252, 165, 165, 0.18) 50%,
rgba(239, 68, 68, 0.12) 100%);
border: 1px solid rgba(239, 68, 68, 0.4);
box-shadow:
0 32px 64px rgba(239, 68, 68, 0.2),
0 12px 24px rgba(239, 68, 68, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
0 0 0 1px rgba(239, 68, 68, 0.3);
} }
.notification-warning { .notification-warning {
@apply border-yellow-500; @apply text-yellow-100;
background: linear-gradient(135deg,
rgba(245, 158, 11, 0.25) 0%,
rgba(252, 211, 77, 0.18) 50%,
rgba(245, 158, 11, 0.12) 100%);
border: 1px solid rgba(245, 158, 11, 0.4);
box-shadow:
0 32px 64px rgba(245, 158, 11, 0.2),
0 12px 24px rgba(245, 158, 11, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
0 0 0 1px rgba(245, 158, 11, 0.3);
} }
.notification-info { .notification-info {
@apply border-blue-500; @apply text-blue-100;
background: linear-gradient(135deg,
rgba(59, 130, 246, 0.25) 0%,
rgba(147, 197, 253, 0.18) 50%,
rgba(59, 130, 246, 0.12) 100%);
border: 1px solid rgba(59, 130, 246, 0.4);
box-shadow:
0 32px 64px rgba(59, 130, 246, 0.2),
0 12px 24px rgba(59, 130, 246, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
0 0 0 1px rgba(59, 130, 246, 0.3);
} }
/* Alerts */ /* Toast-Benachrichtigungen - Einheitlich */
.toast-notification {
@apply fixed z-50 p-4 rounded-2xl shadow-2xl transform transition-all duration-500 text-sm font-medium;
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%);
-webkit-backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%);
border: 1px solid rgba(255, 255, 255, 0.25);
box-shadow:
0 32px 64px rgba(0, 0, 0, 0.25),
0 12px 24px rgba(0, 0, 0, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
0 0 0 1px rgba(255, 255, 255, 0.1);
}
.dark .toast-notification {
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%);
-webkit-backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow:
0 32px 64px rgba(0, 0, 0, 0.6),
0 12px 24px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.2),
0 0 0 1px rgba(255, 255, 255, 0.05);
}
/* Alert-Benachrichtigungen - Verbessert */
.alert { .alert {
@apply p-4 rounded-lg border mb-4; @apply p-6 rounded-2xl border mb-6 shadow-2xl;
background: rgba(255, 255, 255, 0.12);
backdrop-filter: blur(30px) saturate(200%) brightness(120%) contrast(110%);
-webkit-backdrop-filter: blur(30px) saturate(200%) brightness(120%) contrast(110%);
border: 1px solid rgba(255, 255, 255, 0.25);
box-shadow:
0 25px 50px rgba(0, 0, 0, 0.15),
0 8px 16px rgba(0, 0, 0, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.3),
0 0 0 1px rgba(255, 255, 255, 0.1);
animation: alert-fade-in 0.5s ease-out;
} }
.dark .alert {
background: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(30px) saturate(180%) brightness(110%) contrast(120%);
-webkit-backdrop-filter: blur(30px) saturate(180%) brightness(110%) contrast(120%);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow:
0 25px 50px rgba(0, 0, 0, 0.4),
0 8px 16px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.15),
0 0 0 1px rgba(255, 255, 255, 0.05);
}
.alert-success { .alert-success {
@apply bg-green-50 border-green-500 text-green-800 dark:bg-green-900/30 dark:text-green-200; @apply text-green-900 dark:text-green-100;
background: linear-gradient(135deg,
rgba(34, 197, 94, 0.15) 0%,
rgba(134, 239, 172, 0.1) 50%,
rgba(34, 197, 94, 0.08) 100%);
border: 1px solid rgba(34, 197, 94, 0.3);
} }
.alert-error { .alert-error {
@apply bg-red-50 border-red-500 text-red-800 dark:bg-red-900/30 dark:text-red-200; @apply text-red-900 dark:text-red-100;
background: linear-gradient(135deg,
rgba(239, 68, 68, 0.15) 0%,
rgba(252, 165, 165, 0.1) 50%,
rgba(239, 68, 68, 0.08) 100%);
border: 1px solid rgba(239, 68, 68, 0.3);
} }
.alert-warning { .alert-warning {
@apply bg-yellow-50 border-yellow-500 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-200; @apply text-yellow-900 dark:text-yellow-100;
background: linear-gradient(135deg,
rgba(245, 158, 11, 0.15) 0%,
rgba(252, 211, 77, 0.1) 50%,
rgba(245, 158, 11, 0.08) 100%);
border: 1px solid rgba(245, 158, 11, 0.3);
} }
.alert-info { .alert-info {
@apply bg-blue-50 border-blue-500 text-blue-800 dark:bg-blue-900/30 dark:text-blue-200; @apply text-blue-900 dark:text-blue-100;
background: linear-gradient(135deg,
rgba(59, 130, 246, 0.15) 0%,
rgba(147, 197, 253, 0.1) 50%,
rgba(59, 130, 246, 0.08) 100%);
border: 1px solid rgba(59, 130, 246, 0.3);
}
/* Browser-Notification-Container */
.browser-notification {
@apply fixed top-4 left-4 max-w-sm p-4 rounded-2xl shadow-2xl z-50;
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%);
-webkit-backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%);
border: 1px solid rgba(255, 255, 255, 0.25);
box-shadow:
0 32px 64px rgba(0, 0, 0, 0.25),
0 12px 24px rgba(0, 0, 0, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 0.4),
0 0 0 1px rgba(255, 255, 255, 0.1);
animation: notification-slide-left 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.dark .browser-notification {
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%);
-webkit-backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow:
0 32px 64px rgba(0, 0, 0, 0.6),
0 12px 24px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.2),
0 0 0 1px rgba(255, 255, 255, 0.05);
}
/* Notification-Animationen */
@keyframes notification-slide-in {
0% {
opacity: 0;
transform: translateX(100%) translateY(-20px) scale(0.9);
backdrop-filter: blur(0px);
}
50% {
opacity: 0.8;
transform: translateX(20px) translateY(-10px) scale(1.05);
backdrop-filter: blur(20px);
}
100% {
opacity: 1;
transform: translateX(0) translateY(0) scale(1);
backdrop-filter: blur(40px);
}
}
@keyframes notification-slide-out {
0% {
opacity: 1;
transform: translateX(0) translateY(0) scale(1);
}
100% {
opacity: 0;
transform: translateX(100%) translateY(-20px) scale(0.9);
}
}
@keyframes notification-slide-left {
0% {
opacity: 0;
transform: translateX(-100%) translateY(-20px) scale(0.9);
backdrop-filter: blur(0px);
}
50% {
opacity: 0.8;
transform: translateX(-20px) translateY(-10px) scale(1.05);
backdrop-filter: blur(20px);
}
100% {
opacity: 1;
transform: translateX(0) translateY(0) scale(1);
backdrop-filter: blur(40px);
}
}
@keyframes alert-fade-in {
0% {
opacity: 0;
transform: translateY(-20px) scale(0.95);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.notification.hiding {
animation: notification-slide-out 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
/* Notification-Icons mit Glassmorphism */
.notification-icon {
@apply flex items-center justify-center w-8 h-8 rounded-full mr-3 flex-shrink-0;
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow:
0 8px 16px rgba(0, 0, 0, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.4);
}
.notification-content {
@apply flex-1;
}
.notification-title {
@apply font-semibold text-sm mb-1;
}
.notification-message {
@apply text-sm opacity-90;
}
.notification-close {
@apply ml-3 p-1 rounded-lg opacity-70 hover:opacity-100 transition-opacity;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.notification-close:hover {
background: rgba(255, 255, 255, 0.2);
transform: scale(1.1);
}
/* Multiple Notifications Container */
.notifications-container {
@apply fixed top-4 right-4 z-50 space-y-3 max-w-md;
}
.notifications-container-left {
@apply fixed top-4 left-4 z-50 space-y-3 max-w-sm;
}
/* Flash-Message-Verbesserungen - Vereinheitlicht */
.flash-message-light {
background: linear-gradient(135deg,
rgba(255, 255, 255, 0.95) 0%,
rgba(248, 250, 252, 0.9) 100%);
backdrop-filter: blur(32px) saturate(200%) brightness(120%);
-webkit-backdrop-filter: blur(32px) saturate(200%) brightness(120%);
border: 1px solid rgba(226, 232, 240, 0.6);
box-shadow:
0 25px 50px rgba(0, 0, 0, 0.1),
0 12px 24px rgba(0, 115, 206, 0.05),
inset 0 1px 0 rgba(255, 255, 255, 0.8);
color: var(--color-text-primary);
}
.flash-message-light.success {
border-left: 4px solid #10b981;
background: linear-gradient(135deg,
rgba(236, 253, 245, 0.95) 0%,
rgba(209, 250, 229, 0.9) 100%);
}
.flash-message-light.error {
border-left: 4px solid #ef4444;
background: linear-gradient(135deg,
rgba(254, 242, 242, 0.95) 0%,
rgba(252, 165, 165, 0.9) 100%);
}
.flash-message-light.warning {
border-left: 4px solid #fbbf24;
background: linear-gradient(135deg,
rgba(255, 251, 235, 0.95) 0%,
rgba(254, 243, 199, 0.9) 100%);
}
.flash-message-light.info {
border-left: 4px solid #3b82f6;
background: linear-gradient(135deg,
rgba(239, 246, 255, 0.95) 0%,
rgba(219, 234, 254, 0.9) 100%);
}
/* Premium Table Styles */
.table-enhanced {
background: var(--gradient-card);
border: 1px solid var(--color-border-primary);
border-radius: var(--card-radius);
overflow: hidden;
box-shadow:
0 4px 20px var(--color-shadow),
0 2px 8px rgba(0, 115, 206, 0.04),
inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
.table-enhanced th {
background: linear-gradient(135deg,
var(--color-bg-secondary) 0%,
var(--color-bg-tertiary) 100%);
color: var(--color-text-primary);
font-weight: 600;
padding: 1rem 1.5rem;
border-bottom: 1px solid var(--color-border-primary);
position: relative;
}
.table-enhanced th::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg,
transparent 0%,
var(--color-border-secondary) 50%,
transparent 100%);
}
.table-enhanced td {
padding: 1rem 1.5rem;
border-bottom: 1px solid var(--color-border-primary);
color: var(--color-text-secondary);
transition: all 0.2s ease;
}
.table-enhanced tbody tr:hover {
background: var(--color-bg-secondary);
transform: scale(1.002);
}
.dark .table-enhanced {
background: rgba(10, 10, 10, 0.8);
border-color: var(--color-border-primary);
}
.dark .table-enhanced th {
background: rgba(26, 26, 26, 0.8);
color: var(--color-text-primary);
}
.dark .table-enhanced tbody tr:hover {
background: rgba(26, 26, 26, 0.6);
}
/* Premium Modal Styles */
.modal-enhanced {
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: 1.5rem;
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);
position: relative;
overflow: hidden;
}
.modal-enhanced::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%);
}
.dark .modal-enhanced {
background: rgba(0, 0, 0, 0.95);
border-color: rgba(42, 42, 42, 0.7);
box-shadow:
0 50px 100px rgba(0, 0, 0, 0.5),
inset 0 2px 0 rgba(255, 255, 255, 0.05);
}
/* Enhanced Status Badges */
.status-badge-enhanced {
display: inline-flex;
align-items: center;
padding: 0.5rem 1rem;
font-size: 0.75rem;
font-weight: 700;
border-radius: 9999px;
text-transform: uppercase;
letter-spacing: 0.05em;
border: 1px solid transparent;
transition: all 0.2s ease;
position: relative;
overflow: hidden;
}
.status-badge-enhanced::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
transition: left 0.5s ease;
}
.status-badge-enhanced:hover::before {
left: 100%;
}
.status-online-enhanced {
background: linear-gradient(135deg, #ecfdf5 0%, #a7f3d0 100%);
color: #065f46;
border-color: rgba(16, 185, 129, 0.3);
}
.status-offline-enhanced {
background: linear-gradient(135deg, #fef2f2 0%, #fca5a5 100%);
color: #991b1b;
border-color: rgba(239, 68, 68, 0.3);
}
.status-printing-enhanced {
background: linear-gradient(135deg, #eff6ff 0%, #bfdbfe 100%);
color: #1e40af;
border-color: rgba(59, 130, 246, 0.3);
}
/* Dark Mode Toggle - Premium Design */
.dark-mode-toggle-new {
position: relative;
display: flex;
cursor: pointer;
align-items: center;
justify-content: center;
border-radius: 9999px;
padding: 0.625rem;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
background: linear-gradient(135deg,
rgba(248, 250, 252, 0.9) 0%,
rgba(241, 245, 249, 0.8) 100%);
border: 1px solid rgba(226, 232, 240, 0.7);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.06),
0 2px 4px rgba(0, 115, 206, 0.04),
inset 0 1px 0 rgba(255, 255, 255, 0.8);
color: var(--color-text-secondary);
z-index: 100;
}
.dark-mode-toggle-new:hover {
transform: translateY(-2px) scale(1.05);
background: linear-gradient(135deg,
rgba(248, 250, 252, 0.95) 0%,
rgba(241, 245, 249, 0.85) 100%);
box-shadow:
0 8px 20px rgba(0, 0, 0, 0.1),
0 4px 8px rgba(0, 115, 206, 0.08),
inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
.dark-mode-toggle-new:active {
transform: translateY(-1px) scale(0.98);
transition: transform 0.1s;
}
.dark .dark-mode-toggle-new {
background: rgba(10, 10, 10, 0.8);
border: 1px solid rgba(42, 42, 42, 0.6);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.05);
color: var(--color-text-secondary);
}
.dark .dark-mode-toggle-new:hover {
background: rgba(10, 10, 10, 0.9);
box-shadow:
0 8px 20px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
/* Icon-Animation für Dark Mode Toggle */
.dark-mode-toggle-new .sun-icon,
.dark-mode-toggle-new .moon-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.dark-mode-toggle-new .sun-icon:not(.hidden) {
animation: icon-appear 0.5s cubic-bezier(0.25, 1, 0.5, 1) forwards;
}
.dark-mode-toggle-new .moon-icon:not(.hidden) {
animation: icon-appear 0.5s cubic-bezier(0.25, 1, 0.5, 1) forwards;
}
@keyframes icon-appear {
0% {
opacity: 0;
transform: translate(-50%, -50%) scale(0.5) rotate(-20deg);
}
100% {
opacity: 1;
transform: translate(-50%, -50%) scale(1) rotate(0);
}
}
.dark .sun-icon { display: none; }
.dark .moon-icon { display: block; }
.sun-icon { display: block; }
.moon-icon { display: none; }
/* User Menu Button - Premium Design */
.user-menu-button-new {
display: flex;
align-items: center;
gap: 0.5rem;
border-radius: 0.75rem;
padding: 0.5rem;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
background: linear-gradient(135deg,
rgba(248, 250, 252, 0.8) 0%,
rgba(241, 245, 249, 0.7) 100%);
border: 1px solid rgba(226, 232, 240, 0.6);
box-shadow:
0 2px 8px rgba(0, 0, 0, 0.05),
inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
.user-menu-button-new:hover {
transform: translateY(-1px);
background: linear-gradient(135deg,
rgba(248, 250, 252, 0.9) 0%,
rgba(241, 245, 249, 0.8) 100%);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.08),
0 2px 4px rgba(0, 115, 206, 0.04),
inset 0 1px 0 rgba(255, 255, 255, 0.8);
}
.dark .user-menu-button-new {
background: rgba(10, 10, 10, 0.7);
border-color: rgba(42, 42, 42, 0.6);
box-shadow:
0 2px 8px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.03);
}
.dark .user-menu-button-new:hover {
background: rgba(10, 10, 10, 0.8);
box-shadow:
0 4px 12px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
/* Enhanced Hover Effects */
.hover-lift-enhanced {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.hover-lift-enhanced:hover {
transform: translateY(-3px) scale(1.01);
box-shadow:
0 12px 30px var(--color-shadow-strong),
0 6px 15px var(--color-shadow-accent);
}
.dark .hover-lift-enhanced:hover {
box-shadow: 0 12px 30px var(--color-shadow);
}
/* Smooth Scrollbar for Light Mode */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--color-bg-secondary);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: linear-gradient(180deg,
var(--color-border-secondary) 0%,
var(--color-border-primary) 100%);
border-radius: 4px;
transition: background 0.2s ease;
}
::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg,
var(--color-accent) 0%,
var(--color-accent-hover) 100%);
}
.dark ::-webkit-scrollbar-track {
background: var(--color-bg-secondary);
}
.dark ::-webkit-scrollbar-thumb {
background: var(--color-border-primary);
}
.dark ::-webkit-scrollbar-thumb:hover {
background: #60a5fa;
}
/* Loading States */
.loading-enhanced {
position: relative;
overflow: hidden;
}
.loading-enhanced::after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg,
transparent,
rgba(0, 115, 206, 0.1),
transparent);
animation: loading-shimmer 2s infinite;
}
@keyframes loading-shimmer {
0% { left: -100%; }
100% { left: 100%; }
}
/* Focus States for Accessibility */
.focus-enhanced:focus {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
box-shadow:
0 0 0 4px rgba(0, 115, 206, 0.15),
0 4px 12px var(--color-shadow-accent);
}
.dark .focus-enhanced:focus {
outline-color: #60a5fa;
box-shadow:
0 0 0 4px rgba(96, 165, 250, 0.15),
0 4px 12px rgba(96, 165, 250, 0.2);
}
/* Responsive Design Enhancements */
@media (max-width: 768px) {
.card-enhanced {
padding: 1rem;
border-radius: 0.75rem;
}
.btn-enhanced {
padding: 0.75rem 1.5rem;
font-size: 0.8rem;
}
.modal-enhanced {
border-radius: 1rem;
margin: 1rem;
}
.dark-mode-toggle-new {
padding: 0.5rem;
}
}
/* Reduced Motion Support */
@media (prefers-reduced-motion: reduce) {
* {
transition: none !important;
animation: none !important;
}
}
/* High Contrast Mode Support */
@media (prefers-contrast: high) {
:root {
--color-shadow: rgba(0, 0, 0, 0.2);
--color-shadow-strong: rgba(0, 0, 0, 0.3);
--color-border-primary: #000000;
}
.dark {
--color-border-primary: #ffffff;
}
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -181,7 +181,7 @@ window.refreshStats = async function() {
}; };
/** /**
* Jobs-Refresh-Funktion * Jobs-Refresh-Funktion - VERBESSERT mit umfassenden Null-Checks
*/ */
window.refreshJobs = async function() { window.refreshJobs = async function() {
const refreshButton = document.getElementById('refresh-button'); const refreshButton = document.getElementById('refresh-button');
@ -194,14 +194,27 @@ window.refreshJobs = async function() {
} }
try { try {
// Jobs-Daten neu laden mit verbesserter Fehlerbehandlung console.log('🔄 Starte Jobs-Refresh...');
if (typeof window.jobManager !== 'undefined' && window.jobManager && window.jobManager.loadJobs) {
// Mehrstufige Manager-Prüfung mit erweiterten Sicherheitschecks
let refreshSuccess = false;
if (typeof window.jobManager !== 'undefined' &&
window.jobManager &&
typeof window.jobManager.loadJobs === 'function') {
console.log('📝 Verwende window.jobManager.loadJobs()');
await window.jobManager.loadJobs(); await window.jobManager.loadJobs();
} else if (typeof jobManager !== 'undefined' && jobManager && jobManager.loadJobs) { refreshSuccess = true;
} else if (typeof jobManager !== 'undefined' &&
jobManager &&
typeof jobManager.loadJobs === 'function') {
console.log('📝 Verwende lokalen jobManager.loadJobs()');
await jobManager.loadJobs(); await jobManager.loadJobs();
refreshSuccess = true;
} else { } else {
// Fallback: API-Aufruf direkt // Fallback: Direkter API-Aufruf mit verbesserter Fehlerbehandlung
console.log('📝 JobManager nicht verfügbar - verwende direkten API-Aufruf'); console.log('📝 JobManager nicht verfügbar - verwende direkten API-Aufruf');
const response = await fetch('/api/jobs', { const response = await fetch('/api/jobs', {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -214,28 +227,128 @@ window.refreshJobs = async function() {
} }
const data = await response.json(); const data = await response.json();
console.log('📝 Jobs erfolgreich über API geladen:', data); console.log('📝 API-Response erhalten:', data);
// Wenn JobsContainer vorhanden ist, versuche die Jobs zu rendern // VERBESSERTE Datenvalidierung
const jobsContainer = document.querySelector('.jobs-container, #jobs-container, .job-grid'); if (!data || typeof data !== 'object') {
if (jobsContainer && data.jobs) { throw new Error('Ungültige API-Response: Keine Daten erhalten');
// Einfache Jobs-Darstellung als Fallback
jobsContainer.innerHTML = data.jobs.map(job => `
<div class="job-card p-4 border rounded-lg">
<h3 class="font-semibold">${job.filename || 'Unbekannter Job'}</h3>
<p class="text-sm text-gray-600">Status: ${job.status || 'Unbekannt'}</p>
</div>
`).join('');
} }
// Sichere Jobs-Extraktion
let jobs = [];
if (Array.isArray(data.jobs)) {
jobs = data.jobs;
} else if (Array.isArray(data)) {
jobs = data;
} else if (data.success && Array.isArray(data.data)) {
jobs = data.data;
} else {
console.warn('⚠️ Keine Jobs-Array in API-Response gefunden:', data);
jobs = [];
}
console.log(`📝 ${jobs.length} Jobs aus API extrahiert:`, jobs);
// Container-Updates mit Fallback-Rendering
const jobsContainers = [
'.jobs-container',
'#jobs-container',
'.job-grid',
'#jobs-list',
'#jobs-grid'
];
let containerFound = false;
for (const selector of jobsContainers) {
const container = document.querySelector(selector);
if (container) {
containerFound = true;
console.log(`📝 Container gefunden: ${selector}`);
if (jobs.length === 0) {
container.innerHTML = `
<div class="text-center py-12">
<div class="text-gray-400 dark:text-gray-600 text-6xl mb-4">📭</div>
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">Keine Jobs vorhanden</h3>
<p class="text-gray-500 dark:text-gray-400">Es wurden noch keine Druckaufträge erstellt.</p>
</div>
`;
} else {
// Sichere Job-Darstellung mit Null-Checks
const jobCards = jobs.map(job => {
if (!job || typeof job !== 'object') {
console.warn('⚠️ Ungültiges Job-Objekt übersprungen:', job);
return '';
}
return `
<div class="job-card p-4 border rounded-lg bg-white dark:bg-slate-800 shadow-sm hover:shadow-md transition-shadow">
<h3 class="font-semibold text-gray-900 dark:text-white mb-2">
${job.filename || job.title || job.name || 'Unbekannter Job'}
</h3>
<div class="text-sm text-gray-600 dark:text-gray-400 space-y-1">
<p><span class="font-medium">ID:</span> ${job.id || 'N/A'}</p>
<p><span class="font-medium">Status:</span> ${job.status || 'Unbekannt'}</p>
${job.printer_name ? `<p><span class="font-medium">Drucker:</span> ${job.printer_name}</p>` : ''}
${job.created_at ? `<p><span class="font-medium">Erstellt:</span> ${new Date(job.created_at).toLocaleDateString('de-DE')}</p>` : ''}
</div>
</div>
`;
}).filter(card => card !== '').join('');
container.innerHTML = jobCards || `
<div class="text-center py-8">
<p class="text-gray-500 dark:text-gray-400">Keine gültigen Jobs zum Anzeigen</p>
</div>
`;
}
break;
}
}
if (!containerFound) {
console.warn('⚠️ Kein Jobs-Container gefunden. Verfügbare Container:', jobsContainers);
}
refreshSuccess = true;
}
if (refreshSuccess) {
showToast('✅ Druckaufträge erfolgreich aktualisiert', 'success');
} }
showToast('✅ Druckaufträge erfolgreich aktualisiert', 'success');
} catch (error) { } catch (error) {
console.error('Jobs-Refresh Fehler:', error); console.error('❌ Jobs-Refresh Fehler:', error);
const errorMessage = error.message === 'undefined' ?
'Jobs-Manager nicht verfügbar' : // Intelligente Fehlermeldung basierend auf dem Fehlertyp
error.message || 'Unbekannter Fehler'; let errorMessage;
showToast(`❌ Fehler beim Laden der Jobs: ${errorMessage}`, 'error'); if (error.message.includes('undefined')) {
errorMessage = 'Jobs-Daten nicht verfügbar';
} else if (error.message.includes('fetch')) {
errorMessage = 'Netzwerkfehler beim Laden der Jobs';
} else if (error.message.includes('API')) {
errorMessage = 'Server-Fehler beim Laden der Jobs';
} else {
errorMessage = error.message || 'Unbekannter Fehler beim Laden der Jobs';
}
showToast(`❌ Fehler: ${errorMessage}`, 'error');
// Fallback-Container mit Fehleranzeige
const container = document.querySelector('.jobs-container, #jobs-container, .job-grid, #jobs-list');
if (container) {
container.innerHTML = `
<div class="text-center py-12">
<div class="text-red-400 dark:text-red-600 text-6xl mb-4"></div>
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">Fehler beim Laden</h3>
<p class="text-gray-500 dark:text-gray-400 mb-4">${errorMessage}</p>
<button onclick="refreshJobs()" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">
Erneut versuchen
</button>
</div>
`;
}
} finally { } finally {
if (refreshButton) { if (refreshButton) {
refreshButton.disabled = false; refreshButton.disabled = false;

View File

@ -382,6 +382,12 @@
return; return;
} }
// VERBESSERTE SICHERHEITSCHECKS
if (!Array.isArray(this.jobs)) {
console.warn('⚠️ this.jobs ist kein Array:', this.jobs);
this.jobs = [];
}
if (this.jobs.length === 0) { if (this.jobs.length === 0) {
jobsList.innerHTML = ` jobsList.innerHTML = `
<div class="text-center py-12"> <div class="text-center py-12">
@ -393,10 +399,32 @@
return; return;
} }
const jobsHTML = this.jobs.map(job => this.renderJobCard(job)).join(''); try {
jobsList.innerHTML = jobsHTML; const jobsHTML = this.jobs.map(job => {
// Sicherstellen, dass job ein gültiges Objekt ist
console.log(`📋 ${this.jobs.length} Jobs gerendert`); if (!job || typeof job !== 'object') {
console.warn('⚠️ Ungültiges Job-Objekt:', job);
return '';
}
return this.renderJobCard(job);
}).filter(html => html !== '').join('');
jobsList.innerHTML = jobsHTML;
console.log(`📋 ${this.jobs.length} Jobs gerendert`);
} catch (error) {
console.error('❌ Fehler beim Rendern der Jobs:', error);
jobsList.innerHTML = `
<div class="text-center py-12">
<div class="text-red-400 dark:text-red-600 text-6xl mb-4"></div>
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">Fehler beim Laden</h3>
<p class="text-gray-500 dark:text-gray-400">Es gab einen Fehler beim Darstellen der Jobs.</p>
<button onclick="window.jobManager.loadJobs()" class="mt-4 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
Erneut versuchen
</button>
</div>
`;
}
} }
/** /**

View File

@ -0,0 +1,307 @@
/**
* Jobs Safety Fix - Mercedes-Benz MYP Platform
* Umfassende Lösung für "jobs undefined" Probleme
* Version: 1.0.0
*/
(function() {
'use strict';
console.log('🛡️ Jobs Safety Fix wird geladen...');
/**
* Globale Jobs-Variable sicher initialisieren
*/
if (typeof window.jobsData === 'undefined') {
window.jobsData = [];
}
if (typeof window.filteredJobs === 'undefined') {
window.filteredJobs = [];
}
/**
* Sichere Jobs-Funktionen
*/
window.safeJobsOperations = {
/**
* Jobs sicher abrufen
*/
getJobs: function() {
if (window.jobManager && Array.isArray(window.jobManager.jobs)) {
return window.jobManager.jobs;
}
if (Array.isArray(window.jobsData)) {
return window.jobsData;
}
return [];
},
/**
* Gefilterte Jobs sicher abrufen
*/
getFilteredJobs: function() {
if (Array.isArray(window.filteredJobs)) {
return window.filteredJobs;
}
return this.getJobs();
},
/**
* Jobs sicher setzen
*/
setJobs: function(jobs) {
if (!Array.isArray(jobs)) {
console.warn('⚠️ Jobs ist kein Array, konvertiere zu leerem Array');
jobs = [];
}
if (window.jobManager) {
window.jobManager.jobs = jobs;
}
window.jobsData = jobs;
console.log(`${jobs.length} Jobs sicher gesetzt`);
},
/**
* Job sicher finden
*/
findJob: function(jobId) {
const jobs = this.getJobs();
return jobs.find(job => job && job.id && job.id.toString() === jobId.toString()) || null;
},
/**
* Jobs sicher filtern
*/
filterJobs: function(filterFn) {
const jobs = this.getJobs();
if (typeof filterFn !== 'function') {
return jobs;
}
try {
return jobs.filter(job => {
if (!job || typeof job !== 'object') {
return false;
}
return filterFn(job);
});
} catch (error) {
console.error('❌ Fehler beim Filtern der Jobs:', error);
return jobs;
}
}
};
/**
* JobManager Sicherheitsprüfungen
*/
function ensureJobManagerSafety() {
if (typeof window.jobManager !== 'undefined' && window.jobManager) {
// Sicherstellen, dass jobs ein Array ist
if (!Array.isArray(window.jobManager.jobs)) {
console.warn('⚠️ JobManager.jobs ist kein Array, korrigiere...');
window.jobManager.jobs = [];
}
// Originale loadJobs Methode wrappen
if (window.jobManager.loadJobs && typeof window.jobManager.loadJobs === 'function') {
const originalLoadJobs = window.jobManager.loadJobs;
window.jobManager.loadJobs = async function(...args) {
try {
await originalLoadJobs.apply(this, args);
// Nach dem Laden sicherstellen, dass jobs ein Array ist
if (!Array.isArray(this.jobs)) {
this.jobs = [];
}
} catch (error) {
console.error('❌ Fehler beim sicheren Laden der Jobs:', error);
this.jobs = [];
throw error;
}
};
}
// Originale renderJobs Methode wrappen
if (window.jobManager.renderJobs && typeof window.jobManager.renderJobs === 'function') {
const originalRenderJobs = window.jobManager.renderJobs;
window.jobManager.renderJobs = function(...args) {
try {
// Sicherstellen, dass jobs ein Array ist vor dem Rendern
if (!Array.isArray(this.jobs)) {
console.warn('⚠️ Jobs ist kein Array vor dem Rendern, korrigiere...');
this.jobs = [];
}
return originalRenderJobs.apply(this, args);
} catch (error) {
console.error('❌ Fehler beim sicheren Rendern der Jobs:', error);
// Fallback-Rendering
const jobsList = document.getElementById('jobs-list');
if (jobsList) {
jobsList.innerHTML = `
<div class="text-center py-12">
<div class="text-red-400 dark:text-red-600 text-6xl mb-4"></div>
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">Renderingfehler</h3>
<p class="text-gray-500 dark:text-gray-400 mb-4">Jobs konnten nicht angezeigt werden</p>
<button onclick="window.jobManager.loadJobs()" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
Erneut laden
</button>
</div>
`;
}
}
};
}
}
}
/**
* Globale Variablen-Überwachung
*/
function setupGlobalVariableWatching() {
// jobsData überwachen
let _jobsData = [];
Object.defineProperty(window, 'jobsData', {
get: function() {
return _jobsData;
},
set: function(value) {
if (!Array.isArray(value)) {
console.warn('⚠️ Versuche jobsData mit Non-Array zu setzen:', value);
_jobsData = [];
} else {
_jobsData = value;
}
},
enumerable: true,
configurable: true
});
// filteredJobs überwachen
let _filteredJobs = [];
Object.defineProperty(window, 'filteredJobs', {
get: function() {
return _filteredJobs;
},
set: function(value) {
if (!Array.isArray(value)) {
console.warn('⚠️ Versuche filteredJobs mit Non-Array zu setzen:', value);
_filteredJobs = [];
} else {
_filteredJobs = value;
}
},
enumerable: true,
configurable: true
});
}
/**
* API-Response Validator
*/
window.validateJobsResponse = function(data) {
if (!data || typeof data !== 'object') {
console.warn('⚠️ Ungültige API-Response:', data);
return { jobs: [], total: 0 };
}
let jobs = [];
// Verschiedene Response-Formate unterstützen
if (Array.isArray(data.jobs)) {
jobs = data.jobs;
} else if (Array.isArray(data.data)) {
jobs = data.data;
} else if (Array.isArray(data)) {
jobs = data;
} else if (data.success && Array.isArray(data.jobs)) {
jobs = data.jobs;
}
// Jobs validieren
jobs = jobs.filter(job => {
if (!job || typeof job !== 'object') {
console.warn('⚠️ Ungültiges Job-Objekt gefiltert:', job);
return false;
}
if (!job.id) {
console.warn('⚠️ Job ohne ID gefiltert:', job);
return false;
}
return true;
});
console.log(`${jobs.length} Jobs validiert`);
return {
jobs: jobs,
total: jobs.length,
current_page: data.current_page || 1,
total_pages: data.total_pages || 1
};
};
/**
* Fehlerbehandlung für undefined Jobs
*/
window.addEventListener('error', function(e) {
if (e.message && e.message.includes('jobs') && e.message.includes('undefined')) {
console.log('🛡️ Jobs undefined Fehler abgefangen:', e.message);
// Versuche Jobs zu reparieren
window.safeJobsOperations.setJobs([]);
e.preventDefault();
return false;
}
});
/**
* Promise rejection handler für Jobs
*/
window.addEventListener('unhandledrejection', function(e) {
if (e.reason && e.reason.message && e.reason.message.includes('jobs')) {
console.log('🛡️ Jobs Promise rejection abgefangen:', e.reason);
// Versuche Jobs zu reparieren
window.safeJobsOperations.setJobs([]);
e.preventDefault();
}
});
/**
* DOM bereit - Setup starten
*/
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
setupGlobalVariableWatching();
ensureJobManagerSafety();
});
} else {
setupGlobalVariableWatching();
ensureJobManagerSafety();
}
/**
* Periodische Sicherheitsprüfung
*/
setInterval(function() {
ensureJobManagerSafety();
// Prüfe ob globale Jobs-Variablen noch Arrays sind
if (!Array.isArray(window.jobsData)) {
console.warn('⚠️ jobsData ist kein Array mehr, repariere...');
window.jobsData = [];
}
if (!Array.isArray(window.filteredJobs)) {
console.warn('⚠️ filteredJobs ist kein Array mehr, repariere...');
window.filteredJobs = [];
}
}, 10000); // Alle 10 Sekunden
console.log('✅ Jobs Safety Fix erfolgreich geladen');
})();

View File

@ -1,9 +1,9 @@
/** /**
* Benachrichtigungssystem für die MYP 3D-Druck Platform * Modernes Benachrichtigungssystem für die MYP 3D-Druck Platform
* Verwaltet die Anzeige und Interaktion mit Benachrichtigungen * Verwaltet einheitliche Glassmorphism-Benachrichtigungen aller Art
*/ */
class NotificationManager { class ModernNotificationManager {
constructor() { constructor() {
this.notificationToggle = document.getElementById('notificationToggle'); this.notificationToggle = document.getElementById('notificationToggle');
this.notificationDropdown = document.getElementById('notificationDropdown'); this.notificationDropdown = document.getElementById('notificationDropdown');
@ -13,11 +13,14 @@ class NotificationManager {
this.isOpen = false; this.isOpen = false;
this.notifications = []; this.notifications = [];
this.activeToasts = new Map();
this.toastCounter = 0;
// CSRF-Token aus Meta-Tag holen // CSRF-Token aus Meta-Tag holen
this.csrfToken = this.getCSRFToken(); this.csrfToken = this.getCSRFToken();
this.init(); this.init();
this.setupGlobalNotificationSystem();
} }
/** /**
@ -307,9 +310,358 @@ class NotificationManager {
console.error('Fehler beim Markieren aller als gelesen:', error); console.error('Fehler beim Markieren aller als gelesen:', error);
} }
} }
setupGlobalNotificationSystem() {
// Globale Funktionen für einheitliche Benachrichtigungen
window.showFlashMessage = this.showGlassToast.bind(this);
window.showToast = this.showGlassToast.bind(this);
window.showNotification = this.showGlassToast.bind(this);
window.showSuccessMessage = (message) => this.showGlassToast(message, 'success');
window.showErrorMessage = (message) => this.showGlassToast(message, 'error');
window.showWarningMessage = (message) => this.showGlassToast(message, 'warning');
window.showInfoMessage = (message) => this.showGlassToast(message, 'info');
}
/**
* Zeigt eine moderne Glassmorphism-Toast-Benachrichtigung
*/
showGlassToast(message, type = 'info', duration = 5000, options = {}) {
// DND-Check
if (window.dndManager && window.dndManager.isEnabled) {
window.dndManager.suppressNotification(message, type);
return;
}
const toastId = `toast-${++this.toastCounter}`;
// Toast-Element erstellen
const toast = document.createElement('div');
toast.id = toastId;
toast.className = `glass-toast notification notification-${type} show`;
// Icon basierend auf Typ
const iconMap = {
success: `<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="M5 13l4 4L19 7"/>
</svg>`,
error: `<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="M6 18L18 6M6 6l12 12"/>
</svg>`,
warning: `<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="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"/>
</svg>`,
info: `<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="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>`
};
toast.innerHTML = `
<div class="flex items-center">
<div class="notification-icon">
${iconMap[type] || iconMap.info}
</div>
<div class="notification-content">
${options.title ? `<div class="notification-title">${options.title}</div>` : ''}
<div class="notification-message">${message}</div>
</div>
<button class="notification-close" onclick="modernNotificationManager.closeToast('${toastId}')">
<svg class="w-4 h-4" 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>
`;
// Container für Toasts erstellen falls nicht vorhanden
let container = document.getElementById('toast-container');
if (!container) {
container = document.createElement('div');
container.id = 'toast-container';
container.className = 'notifications-container';
document.body.appendChild(container);
}
// Position berechnen basierend auf existierenden Toasts
const existingToasts = container.children.length;
toast.style.top = `${1 + existingToasts * 5}rem`;
container.appendChild(toast);
this.activeToasts.set(toastId, toast);
// Hover-Effekt pausiert Auto-Close
let isPaused = false;
let timeoutId;
const startTimer = () => {
if (!isPaused && duration > 0) {
timeoutId = setTimeout(() => {
this.closeToast(toastId);
}, duration);
}
};
toast.addEventListener('mouseenter', () => {
isPaused = true;
clearTimeout(timeoutId);
toast.style.transform = 'translateY(-2px) scale(1.02)';
});
toast.addEventListener('mouseleave', () => {
isPaused = false;
toast.style.transform = 'translateY(0) scale(1)';
startTimer();
});
// Initial timer starten
setTimeout(startTimer, 100);
// Sound-Effekt (optional)
if (options.playSound !== false) {
this.playNotificationSound(type);
}
return toastId;
}
/**
* Schließt eine spezifische Toast-Benachrichtigung
*/
closeToast(toastId) {
const toast = this.activeToasts.get(toastId);
if (!toast) return;
toast.classList.add('hiding');
setTimeout(() => {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
this.activeToasts.delete(toastId);
this.repositionToasts();
}, 400);
}
/**
* Repositioniert alle aktiven Toasts
*/
repositionToasts() {
let index = 0;
this.activeToasts.forEach((toast) => {
if (toast.parentNode) {
toast.style.top = `${1 + index * 5}rem`;
index++;
}
});
}
/**
* Spielt einen Benachrichtigungston ab
*/
playNotificationSound(type) {
try {
// Nur wenn AudioContext verfügbar ist
if (typeof AudioContext !== 'undefined' || typeof webkitAudioContext !== 'undefined') {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
// Verschiedene Töne für verschiedene Typen
const frequencies = {
success: [523.25, 659.25, 783.99], // C-E-G Akkord
error: [440, 415.3], // A-Ab
warning: [493.88, 523.25], // B-C
info: [523.25] // C
};
const freq = frequencies[type] || frequencies.info;
freq.forEach((f, i) => {
setTimeout(() => {
const osc = audioContext.createOscillator();
const gain = audioContext.createGain();
osc.connect(gain);
gain.connect(audioContext.destination);
osc.frequency.setValueAtTime(f, audioContext.currentTime);
gain.gain.setValueAtTime(0.1, audioContext.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1);
osc.start(audioContext.currentTime);
osc.stop(audioContext.currentTime + 0.1);
}, i * 100);
});
}
} catch (error) {
// Silent fail - Audio nicht verfügbar
}
}
/**
* Zeigt Browser-Benachrichtigung (falls berechtigt)
*/
showBrowserNotification(title, message, options = {}) {
if ('Notification' in window) {
if (Notification.permission === 'granted') {
const notification = new Notification(title, {
body: message,
icon: options.icon || '/static/icons/static/icons/notification-icon.png',
badge: options.badge || '/static/icons/static/icons/badge-icon.png',
tag: options.tag || 'myp-notification',
requireInteraction: options.requireInteraction || false,
...options
});
notification.onclick = options.onClick || (() => {
window.focus();
notification.close();
});
return notification;
} else if (Notification.permission === 'default') {
Notification.requestPermission().then(permission => {
if (permission === 'granted') {
this.showBrowserNotification(title, message, options);
}
});
}
}
return null;
}
/**
* Zeigt eine Alert-Benachrichtigung mit Glassmorphism
*/
showAlert(message, type = 'info', options = {}) {
const alertId = `alert-${Date.now()}`;
const alert = document.createElement('div');
alert.id = alertId;
alert.className = `alert alert-${type}`;
alert.innerHTML = `
<div class="flex items-start">
<div class="notification-icon mr-3">
${this.getIconForType(type)}
</div>
<div class="flex-1">
${options.title ? `<h4 class="font-semibold mb-2">${options.title}</h4>` : ''}
<p>${message}</p>
</div>
${options.dismissible !== false ? `
<button class="ml-3 p-1 rounded-lg opacity-70 hover:opacity-100 transition-opacity" onclick="document.getElementById('${alertId}').remove()">
<svg class="w-4 h-4" 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>
`;
// Alert in Container einfügen
const container = options.container || document.querySelector('.flash-messages') || document.body;
container.appendChild(alert);
// Auto-dismiss
if (options.autoDismiss !== false) {
setTimeout(() => {
if (alert.parentNode) {
alert.style.opacity = '0';
alert.style.transform = 'translateY(-20px)';
setTimeout(() => alert.remove(), 300);
}
}, options.duration || 7000);
}
return alertId;
}
getIconForType(type) {
const icons = {
success: `<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
</svg>`,
error: `<svg class="w-5 h-5 text-red-600" 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>`,
warning: `<svg class="w-5 h-5 text-yellow-600" 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.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"/>
</svg>`,
info: `<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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>`
};
return icons[type] || icons.info;
}
} }
// Initialisierung nach DOM-Load // Legacy NotificationManager für Rückwärtskompatibilität
document.addEventListener('DOMContentLoaded', () => { class NotificationManager extends ModernNotificationManager {
new NotificationManager(); constructor() {
}); super();
console.warn('NotificationManager ist deprecated. Verwenden Sie ModernNotificationManager.');
}
}
// Globale Instanz erstellen
const modernNotificationManager = new ModernNotificationManager();
// Für Rückwärtskompatibilität
if (typeof window !== 'undefined') {
window.notificationManager = modernNotificationManager;
window.modernNotificationManager = modernNotificationManager;
}
// CSS für glassmorphe Toasts hinzufügen
const toastStyles = `
<style>
.glass-toast {
position: relative;
transform: translateX(0) translateY(0) scale(1);
animation: toast-slide-in 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.glass-toast:hover {
transform: translateY(-2px) scale(1.02) !important;
transition: transform 0.2s ease;
}
@keyframes toast-slide-in {
0% {
opacity: 0;
transform: translateX(100%) translateY(-20px) scale(0.9);
}
50% {
opacity: 0.8;
transform: translateX(20px) translateY(-10px) scale(1.05);
}
100% {
opacity: 1;
transform: translateX(0) translateY(0) scale(1);
}
}
.notification-item.unread {
background: linear-gradient(135deg,
rgba(59, 130, 246, 0.08) 0%,
rgba(147, 197, 253, 0.05) 100%);
border-left: 3px solid rgb(59, 130, 246);
}
.dark .notification-item.unread {
background: linear-gradient(135deg,
rgba(59, 130, 246, 0.15) 0%,
rgba(147, 197, 253, 0.08) 100%);
}
</style>
`;
// Styles zur Seite hinzufügen
if (typeof document !== 'undefined' && !document.getElementById('toast-styles')) {
const styleElement = document.createElement('div');
styleElement.id = 'toast-styles';
styleElement.innerHTML = toastStyles;
document.head.appendChild(styleElement);
}

View File

@ -115,11 +115,16 @@
if (messageType === 'danger') messageType = 'error'; if (messageType === 'danger') messageType = 'error';
// Nachricht über das moderne Glassmorphism-System anzeigen // Nachricht über das moderne Glassmorphism-System anzeigen
if (typeof showFlashMessage === 'function') { if (typeof showToast === 'function') {
// Kleine Verzögerung für bessere UX // Kleine Verzögerung für bessere UX und schöne Glassmorphism-Animationen
setTimeout(() => { setTimeout(() => {
showFlashMessage(message, messageType, 6000); showToast(message, messageType, 6000, {
}, i * 200); // Nachrichten gestaffelt anzeigen title: messageType === 'success' ? 'Erfolgreich' :
messageType === 'error' ? 'Fehler' :
messageType === 'warning' ? 'Warnung' : 'Information',
playSound: true
});
}, i * 250); // Nachrichten gestaffelt anzeigen
} }
} }
} }
@ -769,39 +774,82 @@
<script src="{{ url_for('static', filename='js/auto-logout.js') }}"></script> <script src="{{ url_for('static', filename='js/auto-logout.js') }}"></script>
{% endif %} {% endif %}
<!-- Glassmorphism Notification System - Modernisiert -->
<script src="{{ url_for('static', filename='js/glassmorphism-notifications.js') }}"></script>
<!-- Additional JavaScript Functions --> <!-- Additional JavaScript Functions -->
<script> <script>
/** /**
* Logout-Handler für sicheres Abmelden * Logout-Handler für sicheres Abmelden
*/ */
function handleLogout() { function handleLogout() {
// Bestätigung abfragen // Verwende das moderne Glassmorphism-Bestätigungssystem
if (confirm('Möchten Sie sich wirklich abmelden?')) { if (typeof showConfirmationToast === 'function') {
// Loading-Animation anzeigen showConfirmationToast(
document.body.style.opacity = '0.7'; 'Möchten Sie sich wirklich abmelden?',
document.body.style.pointerEvents = 'none'; () => {
// Loading-Animation anzeigen
// CSRF-Token aus Meta-Tag holen document.body.style.opacity = '0.7';
const csrfToken = document.querySelector('meta[name="csrf-token"]'); document.body.style.pointerEvents = 'none';
// Logout-Formular erstellen und absenden // CSRF-Token aus Meta-Tag holen
const form = document.createElement('form'); const csrfToken = document.querySelector('meta[name="csrf-token"]');
form.method = 'POST';
form.action = '{{ url_for("auth_logout") }}'; // Logout-Formular erstellen und absenden
form.style.display = 'none'; const form = document.createElement('form');
form.method = 'POST';
// CSRF-Token hinzufügen falls verfügbar form.action = '{{ url_for("auth_logout") }}';
if (csrfToken) { form.style.display = 'none';
const input = document.createElement('input');
input.type = 'hidden'; // CSRF-Token hinzufügen falls verfügbar
input.name = 'csrf_token'; if (csrfToken) {
input.value = csrfToken.getAttribute('content'); const input = document.createElement('input');
form.appendChild(input); input.type = 'hidden';
input.name = 'csrf_token';
input.value = csrfToken.getAttribute('content');
form.appendChild(input);
}
// Formular absenden
document.body.appendChild(form);
form.submit();
},
null,
{
title: 'Abmeldung bestätigen',
confirmText: 'Abmelden',
cancelText: 'Abbrechen'
}
);
} else {
// Fallback für den Fall, dass das Glassmorphism-System nicht verfügbar ist
if (confirm('Möchten Sie sich wirklich abmelden?')) {
// Loading-Animation anzeigen
document.body.style.opacity = '0.7';
document.body.style.pointerEvents = 'none';
// CSRF-Token aus Meta-Tag holen
const csrfToken = document.querySelector('meta[name="csrf-token"]');
// Logout-Formular erstellen und absenden
const form = document.createElement('form');
form.method = 'POST';
form.action = '{{ url_for("auth_logout") }}';
form.style.display = 'none';
// CSRF-Token hinzufügen falls verfügbar
if (csrfToken) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'csrf_token';
input.value = csrfToken.getAttribute('content');
form.appendChild(input);
}
// Formular absenden
document.body.appendChild(form);
form.submit();
} }
// Formular absenden
document.body.appendChild(form);
form.submit();
} }
} }
@ -832,11 +880,16 @@
if (messageType === 'danger') messageType = 'error'; if (messageType === 'danger') messageType = 'error';
// Nachricht über das moderne Glassmorphism-System anzeigen // Nachricht über das moderne Glassmorphism-System anzeigen
if (typeof showFlashMessage === 'function') { if (typeof showToast === 'function') {
// Kleine Verzögerung für bessere UX // Kleine Verzögerung für bessere UX und schöne Glassmorphism-Animationen
setTimeout(() => { setTimeout(() => {
showFlashMessage(message, messageType, 6000); showToast(message, messageType, 6000, {
}, i * 200); // Nachrichten gestaffelt anzeigen title: messageType === 'success' ? 'Erfolgreich' :
messageType === 'error' ? 'Fehler' :
messageType === 'warning' ? 'Warnung' : 'Information',
playSound: true
});
}, i * 250); // Nachrichten gestaffelt anzeigen
} }
} }
} }
@ -1029,8 +1082,11 @@
? 'Nicht stören aktiviert - Benachrichtigungen werden unterdrückt' ? 'Nicht stören aktiviert - Benachrichtigungen werden unterdrückt'
: 'Nicht stören deaktiviert - Benachrichtigungen sind wieder aktiv'; : 'Nicht stören deaktiviert - Benachrichtigungen sind wieder aktiv';
if (typeof showFlashMessage === 'function') { if (typeof showToast === 'function') {
showFlashMessage(message, 'info', 3000); showToast(message, 'info', 3000, {
title: 'Information',
playSound: true
});
} }
console.log(`🔕 DND ${this.isEnabled ? 'aktiviert' : 'deaktiviert'}`); console.log(`🔕 DND ${this.isEnabled ? 'aktiviert' : 'deaktiviert'}`);
@ -1078,15 +1134,23 @@
const count = this.suppressedMessages.length; const count = this.suppressedMessages.length;
const summaryMessage = `${count} Benachrichtigung${count > 1 ? 'en' : ''} während "Nicht stören" erhalten`; const summaryMessage = `${count} Benachrichtigung${count > 1 ? 'en' : ''} während "Nicht stören" erhalten`;
if (typeof showFlashMessage === 'function') { if (typeof showToast === 'function') {
showFlashMessage(summaryMessage, 'info', 4000); showToast(summaryMessage, 'info', 4000, {
title: 'Information',
playSound: true
});
} }
// Optional: Alle einzelnen Nachrichten anzeigen (mit Verzögerung) // Optional: Alle einzelnen Nachrichten anzeigen (mit Verzögerung)
this.suppressedMessages.forEach((item, index) => { this.suppressedMessages.forEach((item, index) => {
setTimeout(() => { setTimeout(() => {
if (typeof showFlashMessage === 'function') { if (typeof showToast === 'function') {
showFlashMessage(item.message, item.type, 3000); showToast(item.message, item.type, 3000, {
title: item.type === 'success' ? 'Erfolgreich' :
item.type === 'error' ? 'Fehler' :
item.type === 'warning' ? 'Warnung' : 'Information',
playSound: true
});
} }
}, (index + 1) * 500); }, (index + 1) * 500);
}); });
@ -1107,15 +1171,15 @@
} }
integrateWithNotificationSystem() { integrateWithNotificationSystem() {
// Überschreibe showFlashMessage wenn verfügbar // Überschreibe showToast wenn verfügbar
if (typeof window.showFlashMessage === 'function') { if (typeof window.showToast === 'function') {
const originalShowFlashMessage = window.showFlashMessage; const originalShowToast = window.showToast;
window.showFlashMessage = (message, type, duration) => { window.showToast = (message, type, duration, options) => {
if (this.shouldSuppressNotification(message, type)) { if (this.shouldSuppressNotification(message, type)) {
console.log(`🔕 Benachrichtigung unterdrückt: ${message}`); console.log(`🔕 Benachrichtigung unterdrückt: ${message}`);
return; return;
} }
return originalShowFlashMessage(message, type, duration); return originalShowToast(message, type, duration, options);
}; };
} }
} }