🎉 Fix for JOBS_UNDEFINED and LOG_EXPORT issues, updated documentation 📚 in backend/docs.
This commit is contained in:
parent
45d8d46556
commit
5ee854cbc6
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.
1
backend/docs/JOBS_UNDEFINED_FIX.md
Normal file
1
backend/docs/JOBS_UNDEFINED_FIX.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
1
backend/docs/LOG_EXPORT_FIX.md
Normal file
1
backend/docs/LOG_EXPORT_FIX.md
Normal file
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
backend/static/css/tailwind.min.css
vendored
2
backend/static/css/tailwind.min.css
vendored
File diff suppressed because one or more lines are too long
1012
backend/static/js/glassmorphism-notifications.js
Normal file
1012
backend/static/js/glassmorphism-notifications.js
Normal file
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
|
||||||
|
if (!job || typeof job !== 'object') {
|
||||||
|
console.warn('⚠️ Ungültiges Job-Objekt:', job);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return this.renderJobCard(job);
|
||||||
|
}).filter(html => html !== '').join('');
|
||||||
|
|
||||||
console.log(`📋 ${this.jobs.length} Jobs gerendert`);
|
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>
|
||||||
|
`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
307
backend/static/js/jobs-safety-fix.js
Normal file
307
backend/static/js/jobs-safety-fix.js
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
/**
|
||||||
|
* Jobs Safety Fix - Mercedes-Benz MYP Platform
|
||||||
|
* Umfassende Lösung für "jobs undefined" Probleme
|
||||||
|
* Version: 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
console.log('🛡️ Jobs Safety Fix wird geladen...');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Globale Jobs-Variable sicher initialisieren
|
||||||
|
*/
|
||||||
|
if (typeof window.jobsData === 'undefined') {
|
||||||
|
window.jobsData = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof window.filteredJobs === 'undefined') {
|
||||||
|
window.filteredJobs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sichere Jobs-Funktionen
|
||||||
|
*/
|
||||||
|
window.safeJobsOperations = {
|
||||||
|
/**
|
||||||
|
* Jobs sicher abrufen
|
||||||
|
*/
|
||||||
|
getJobs: function() {
|
||||||
|
if (window.jobManager && Array.isArray(window.jobManager.jobs)) {
|
||||||
|
return window.jobManager.jobs;
|
||||||
|
}
|
||||||
|
if (Array.isArray(window.jobsData)) {
|
||||||
|
return window.jobsData;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gefilterte Jobs sicher abrufen
|
||||||
|
*/
|
||||||
|
getFilteredJobs: function() {
|
||||||
|
if (Array.isArray(window.filteredJobs)) {
|
||||||
|
return window.filteredJobs;
|
||||||
|
}
|
||||||
|
return this.getJobs();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jobs sicher setzen
|
||||||
|
*/
|
||||||
|
setJobs: function(jobs) {
|
||||||
|
if (!Array.isArray(jobs)) {
|
||||||
|
console.warn('⚠️ Jobs ist kein Array, konvertiere zu leerem Array');
|
||||||
|
jobs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.jobManager) {
|
||||||
|
window.jobManager.jobs = jobs;
|
||||||
|
}
|
||||||
|
window.jobsData = jobs;
|
||||||
|
|
||||||
|
console.log(`✅ ${jobs.length} Jobs sicher gesetzt`);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Job sicher finden
|
||||||
|
*/
|
||||||
|
findJob: function(jobId) {
|
||||||
|
const jobs = this.getJobs();
|
||||||
|
return jobs.find(job => job && job.id && job.id.toString() === jobId.toString()) || null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jobs sicher filtern
|
||||||
|
*/
|
||||||
|
filterJobs: function(filterFn) {
|
||||||
|
const jobs = this.getJobs();
|
||||||
|
if (typeof filterFn !== 'function') {
|
||||||
|
return jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return jobs.filter(job => {
|
||||||
|
if (!job || typeof job !== 'object') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return filterFn(job);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Fehler beim Filtern der Jobs:', error);
|
||||||
|
return jobs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JobManager Sicherheitsprüfungen
|
||||||
|
*/
|
||||||
|
function ensureJobManagerSafety() {
|
||||||
|
if (typeof window.jobManager !== 'undefined' && window.jobManager) {
|
||||||
|
// Sicherstellen, dass jobs ein Array ist
|
||||||
|
if (!Array.isArray(window.jobManager.jobs)) {
|
||||||
|
console.warn('⚠️ JobManager.jobs ist kein Array, korrigiere...');
|
||||||
|
window.jobManager.jobs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Originale loadJobs Methode wrappen
|
||||||
|
if (window.jobManager.loadJobs && typeof window.jobManager.loadJobs === 'function') {
|
||||||
|
const originalLoadJobs = window.jobManager.loadJobs;
|
||||||
|
window.jobManager.loadJobs = async function(...args) {
|
||||||
|
try {
|
||||||
|
await originalLoadJobs.apply(this, args);
|
||||||
|
// Nach dem Laden sicherstellen, dass jobs ein Array ist
|
||||||
|
if (!Array.isArray(this.jobs)) {
|
||||||
|
this.jobs = [];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Fehler beim sicheren Laden der Jobs:', error);
|
||||||
|
this.jobs = [];
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Originale renderJobs Methode wrappen
|
||||||
|
if (window.jobManager.renderJobs && typeof window.jobManager.renderJobs === 'function') {
|
||||||
|
const originalRenderJobs = window.jobManager.renderJobs;
|
||||||
|
window.jobManager.renderJobs = function(...args) {
|
||||||
|
try {
|
||||||
|
// Sicherstellen, dass jobs ein Array ist vor dem Rendern
|
||||||
|
if (!Array.isArray(this.jobs)) {
|
||||||
|
console.warn('⚠️ Jobs ist kein Array vor dem Rendern, korrigiere...');
|
||||||
|
this.jobs = [];
|
||||||
|
}
|
||||||
|
return originalRenderJobs.apply(this, args);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Fehler beim sicheren Rendern der Jobs:', error);
|
||||||
|
// Fallback-Rendering
|
||||||
|
const jobsList = document.getElementById('jobs-list');
|
||||||
|
if (jobsList) {
|
||||||
|
jobsList.innerHTML = `
|
||||||
|
<div class="text-center py-12">
|
||||||
|
<div class="text-red-400 dark:text-red-600 text-6xl mb-4">⚠️</div>
|
||||||
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">Renderingfehler</h3>
|
||||||
|
<p class="text-gray-500 dark:text-gray-400 mb-4">Jobs konnten nicht angezeigt werden</p>
|
||||||
|
<button onclick="window.jobManager.loadJobs()" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
||||||
|
Erneut laden
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Globale Variablen-Überwachung
|
||||||
|
*/
|
||||||
|
function setupGlobalVariableWatching() {
|
||||||
|
// jobsData überwachen
|
||||||
|
let _jobsData = [];
|
||||||
|
Object.defineProperty(window, 'jobsData', {
|
||||||
|
get: function() {
|
||||||
|
return _jobsData;
|
||||||
|
},
|
||||||
|
set: function(value) {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
console.warn('⚠️ Versuche jobsData mit Non-Array zu setzen:', value);
|
||||||
|
_jobsData = [];
|
||||||
|
} else {
|
||||||
|
_jobsData = value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// filteredJobs überwachen
|
||||||
|
let _filteredJobs = [];
|
||||||
|
Object.defineProperty(window, 'filteredJobs', {
|
||||||
|
get: function() {
|
||||||
|
return _filteredJobs;
|
||||||
|
},
|
||||||
|
set: function(value) {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
console.warn('⚠️ Versuche filteredJobs mit Non-Array zu setzen:', value);
|
||||||
|
_filteredJobs = [];
|
||||||
|
} else {
|
||||||
|
_filteredJobs = value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API-Response Validator
|
||||||
|
*/
|
||||||
|
window.validateJobsResponse = function(data) {
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
|
console.warn('⚠️ Ungültige API-Response:', data);
|
||||||
|
return { jobs: [], total: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
let jobs = [];
|
||||||
|
|
||||||
|
// Verschiedene Response-Formate unterstützen
|
||||||
|
if (Array.isArray(data.jobs)) {
|
||||||
|
jobs = data.jobs;
|
||||||
|
} else if (Array.isArray(data.data)) {
|
||||||
|
jobs = data.data;
|
||||||
|
} else if (Array.isArray(data)) {
|
||||||
|
jobs = data;
|
||||||
|
} else if (data.success && Array.isArray(data.jobs)) {
|
||||||
|
jobs = data.jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jobs validieren
|
||||||
|
jobs = jobs.filter(job => {
|
||||||
|
if (!job || typeof job !== 'object') {
|
||||||
|
console.warn('⚠️ Ungültiges Job-Objekt gefiltert:', job);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!job.id) {
|
||||||
|
console.warn('⚠️ Job ohne ID gefiltert:', job);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`✅ ${jobs.length} Jobs validiert`);
|
||||||
|
return {
|
||||||
|
jobs: jobs,
|
||||||
|
total: jobs.length,
|
||||||
|
current_page: data.current_page || 1,
|
||||||
|
total_pages: data.total_pages || 1
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fehlerbehandlung für undefined Jobs
|
||||||
|
*/
|
||||||
|
window.addEventListener('error', function(e) {
|
||||||
|
if (e.message && e.message.includes('jobs') && e.message.includes('undefined')) {
|
||||||
|
console.log('🛡️ Jobs undefined Fehler abgefangen:', e.message);
|
||||||
|
|
||||||
|
// Versuche Jobs zu reparieren
|
||||||
|
window.safeJobsOperations.setJobs([]);
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise rejection handler für Jobs
|
||||||
|
*/
|
||||||
|
window.addEventListener('unhandledrejection', function(e) {
|
||||||
|
if (e.reason && e.reason.message && e.reason.message.includes('jobs')) {
|
||||||
|
console.log('🛡️ Jobs Promise rejection abgefangen:', e.reason);
|
||||||
|
|
||||||
|
// Versuche Jobs zu reparieren
|
||||||
|
window.safeJobsOperations.setJobs([]);
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DOM bereit - Setup starten
|
||||||
|
*/
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
setupGlobalVariableWatching();
|
||||||
|
ensureJobManagerSafety();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setupGlobalVariableWatching();
|
||||||
|
ensureJobManagerSafety();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Periodische Sicherheitsprüfung
|
||||||
|
*/
|
||||||
|
setInterval(function() {
|
||||||
|
ensureJobManagerSafety();
|
||||||
|
|
||||||
|
// Prüfe ob globale Jobs-Variablen noch Arrays sind
|
||||||
|
if (!Array.isArray(window.jobsData)) {
|
||||||
|
console.warn('⚠️ jobsData ist kein Array mehr, repariere...');
|
||||||
|
window.jobsData = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(window.filteredJobs)) {
|
||||||
|
console.warn('⚠️ filteredJobs ist kein Array mehr, repariere...');
|
||||||
|
window.filteredJobs = [];
|
||||||
|
}
|
||||||
|
}, 10000); // Alle 10 Sekunden
|
||||||
|
|
||||||
|
console.log('✅ Jobs Safety Fix erfolgreich geladen');
|
||||||
|
|
||||||
|
})();
|
@ -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);
|
||||||
|
}
|
@ -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
|
||||||
|
document.body.style.opacity = '0.7';
|
||||||
|
document.body.style.pointerEvents = 'none';
|
||||||
|
|
||||||
// CSRF-Token aus Meta-Tag holen
|
// CSRF-Token aus Meta-Tag holen
|
||||||
const csrfToken = document.querySelector('meta[name="csrf-token"]');
|
const csrfToken = document.querySelector('meta[name="csrf-token"]');
|
||||||
|
|
||||||
// Logout-Formular erstellen und absenden
|
// Logout-Formular erstellen und absenden
|
||||||
const form = document.createElement('form');
|
const form = document.createElement('form');
|
||||||
form.method = 'POST';
|
form.method = 'POST';
|
||||||
form.action = '{{ url_for("auth_logout") }}';
|
form.action = '{{ url_for("auth_logout") }}';
|
||||||
form.style.display = 'none';
|
form.style.display = 'none';
|
||||||
|
|
||||||
// CSRF-Token hinzufügen falls verfügbar
|
// CSRF-Token hinzufügen falls verfügbar
|
||||||
if (csrfToken) {
|
if (csrfToken) {
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
input.type = 'hidden';
|
input.type = 'hidden';
|
||||||
input.name = 'csrf_token';
|
input.name = 'csrf_token';
|
||||||
input.value = csrfToken.getAttribute('content');
|
input.value = csrfToken.getAttribute('content');
|
||||||
form.appendChild(input);
|
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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
backend/utils/__pycache__/advanced_tables.cpython-311.pyc
Normal file
BIN
backend/utils/__pycache__/advanced_tables.cpython-311.pyc
Normal file
Binary file not shown.
BIN
backend/utils/__pycache__/email_notification.cpython-311.pyc
Normal file
BIN
backend/utils/__pycache__/email_notification.cpython-311.pyc
Normal file
Binary file not shown.
BIN
backend/utils/__pycache__/maintenance_system.cpython-311.pyc
Normal file
BIN
backend/utils/__pycache__/maintenance_system.cpython-311.pyc
Normal file
Binary file not shown.
BIN
backend/utils/__pycache__/multi_location_system.cpython-311.pyc
Normal file
BIN
backend/utils/__pycache__/multi_location_system.cpython-311.pyc
Normal file
Binary file not shown.
BIN
backend/utils/__pycache__/realtime_dashboard.cpython-311.pyc
Normal file
BIN
backend/utils/__pycache__/realtime_dashboard.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
backend/utils/__pycache__/shutdown_manager.cpython-311.pyc
Normal file
BIN
backend/utils/__pycache__/shutdown_manager.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
backend/utils/__pycache__/timer_manager.cpython-311.pyc
Normal file
BIN
backend/utils/__pycache__/timer_manager.cpython-311.pyc
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user