🎉 Fix for JOBS_UNDEFINED and LOG_EXPORT issues, updated documentation 📚 in backend/docs.
This commit is contained in:
Binary file not shown.
@@ -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.
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 - ==================================================
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+46
-18
@@ -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
|
||||||
|
|||||||
+765
-21
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|
||||||
|
})();
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
+109
-45
@@ -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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user