📝 "Refactor session handling

This commit is contained in:
2025-06-13 07:32:57 +02:00
parent 691a4f2d41
commit eaf415c80f
86 changed files with 603 additions and 720 deletions

Binary file not shown.

View File

@ -772,6 +772,39 @@ def api_update_request(request_id):
logger.error(f"Fehler beim Aktualisieren der Gastanfrage: {str(e)}") logger.error(f"Fehler beim Aktualisieren der Gastanfrage: {str(e)}")
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500 return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
@guest_blueprint.route('/api/admin/requests/<int:request_id>', methods=['DELETE'])
@approver_required
def api_delete_request(request_id):
"""Gastanfrage löschen (nur für Admins)."""
try:
with get_cached_session() as db_session:
guest_request = db_session.query(GuestRequest).filter_by(id=request_id).first()
if not guest_request:
return jsonify({"error": "Anfrage nicht gefunden"}), 404
# Falls ein Job verknüpft ist, diesen auch löschen
if guest_request.job_id:
job = db_session.query(Job).filter_by(id=guest_request.job_id).first()
if job:
db_session.delete(job)
# Gastanfrage löschen
db_session.delete(guest_request)
db_session.commit()
logger.info(f"Gastanfrage {request_id} gelöscht von Admin {current_user.id} ({current_user.username})")
return jsonify({
"success": True,
"message": "Anfrage erfolgreich gelöscht",
"deleted_by": current_user.username,
"deleted_at": datetime.now().isoformat()
})
except Exception as e:
logger.error(f"Fehler beim Löschen der Gastanfrage: {str(e)}")
return jsonify({"error": "Fehler beim Verarbeiten der Anfrage"}), 500
# Admin-Routen # Admin-Routen
@guest_blueprint.route('/admin/requests', methods=['GET']) @guest_blueprint.route('/admin/requests', methods=['GET'])
@approver_required @approver_required

View File

@ -68,3 +68,9 @@
2025-06-13 07:14:31 - [admin] admin - [INFO] INFO - Admin-Check für Funktion tapo_monitoring: User authenticated: True, User ID: 1, Is Admin: True 2025-06-13 07:14:31 - [admin] admin - [INFO] INFO - Admin-Check für Funktion tapo_monitoring: User authenticated: True, User ID: 1, Is Admin: True
2025-06-13 07:14:31 - [admin] admin - [INFO] INFO - Tapo-Monitoring aufgerufen von admin 2025-06-13 07:14:31 - [admin] admin - [INFO] INFO - Tapo-Monitoring aufgerufen von admin
2025-06-13 07:14:44 - [admin] admin - [INFO] INFO - Tapo-Monitoring geladen: 6 Steckdosen, 0 online 2025-06-13 07:14:44 - [admin] admin - [INFO] INFO - Tapo-Monitoring geladen: 6 Steckdosen, 0 online
2025-06-13 07:17:21 - [admin] admin - [INFO] INFO - Admin-Check für Funktion admin_dashboard: User authenticated: True, User ID: 1, Is Admin: True
2025-06-13 07:17:21 - [admin] admin - [INFO] INFO - Admin-Dashboard geladen von admin
2025-06-13 07:17:21 - [admin] admin - [ERROR] ERROR - Fehler beim Laden des Admin-Dashboards: 'dict object' has no attribute 'online_printers'
2025-06-13 07:17:21 - [admin] admin - [INFO] INFO - Admin-Check für Funktion api_admin_system_health_alias: User authenticated: True, User ID: 1, Is Admin: True
2025-06-13 07:17:21 - [admin] admin - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-13 07:17:23 - [admin] admin - [INFO] INFO - Admin-Check für Funktion guest_requests: User authenticated: True, User ID: 1, Is Admin: True

View File

@ -13,3 +13,6 @@
2025-06-13 07:14:28 - [admin_api] admin_api - [ERROR] ERROR - Datenbank-Health-Check fehlgeschlagen: Textual SQL expression 'SELECT 1' should be explicitly declared as text('SELECT 1') 2025-06-13 07:14:28 - [admin_api] admin_api - [ERROR] ERROR - Datenbank-Health-Check fehlgeschlagen: Textual SQL expression 'SELECT 1' should be explicitly declared as text('SELECT 1')
2025-06-13 07:14:28 - [admin_api] admin_api - [ERROR] ERROR - Speicherplatz-Check fehlgeschlagen: module 'os' has no attribute 'statvfs' 2025-06-13 07:14:28 - [admin_api] admin_api - [ERROR] ERROR - Speicherplatz-Check fehlgeschlagen: module 'os' has no attribute 'statvfs'
2025-06-13 07:14:28 - [admin_api] admin_api - [INFO] INFO - System-Health-Check durchgeführt: unhealthy 2025-06-13 07:14:28 - [admin_api] admin_api - [INFO] INFO - System-Health-Check durchgeführt: unhealthy
2025-06-13 07:17:21 - [admin_api] admin_api - [ERROR] ERROR - Datenbank-Health-Check fehlgeschlagen: Textual SQL expression 'SELECT 1' should be explicitly declared as text('SELECT 1')
2025-06-13 07:17:21 - [admin_api] admin_api - [ERROR] ERROR - Speicherplatz-Check fehlgeschlagen: module 'os' has no attribute 'statvfs'
2025-06-13 07:17:21 - [admin_api] admin_api - [INFO] INFO - System-Health-Check durchgeführt: unhealthy

View File

@ -1,3 +1,4 @@
2025-06-13 07:13:53 - [api] api - [INFO] INFO - Statistiken abgerufen von Benutzer admin 2025-06-13 07:13:53 - [api] api - [INFO] INFO - Statistiken abgerufen von Benutzer admin
2025-06-13 07:13:59 - [api] api - [INFO] INFO - Statistiken abgerufen von Benutzer admin 2025-06-13 07:13:59 - [api] api - [INFO] INFO - Statistiken abgerufen von Benutzer admin
2025-06-13 07:14:28 - [api] api - [INFO] INFO - Statistiken abgerufen von Benutzer admin 2025-06-13 07:14:28 - [api] api - [INFO] INFO - Statistiken abgerufen von Benutzer admin
2025-06-13 07:17:21 - [api] api - [INFO] INFO - Statistiken abgerufen von Benutzer admin

View File

@ -22100,3 +22100,232 @@ jinja2.exceptions.UndefinedError: 'stats' is undefined
2025-06-13 07:14:44 - [app] app - [DEBUG] DEBUG - Response: 200 2025-06-13 07:14:44 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:14:50 - [app] app - [DEBUG] DEBUG - Request: GET /dashboard 2025-06-13 07:14:50 - [app] app - [DEBUG] DEBUG - Request: GET /dashboard
2025-06-13 07:14:50 - [app] app - [DEBUG] DEBUG - Response: 200 2025-06-13 07:14:50 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:16:48 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: backend/database/myp.db
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [CONFIG] Erkannte Umgebung: development
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [CONFIG] Production-Modus: False
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [CONFIG] Verwende Development-Konfiguration
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [DEVELOPMENT] Aktiviere Development-Konfiguration
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ MYP Development Environment Konfiguration aktiviert
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ Environment: Development/Testing
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ Debug Mode: True
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ SQL Echo: True
2025-06-13 07:16:49 - [app] app - [INFO] INFO - SQLite für Raspberry Pi optimiert (reduzierte Cache-Größe, SD-Karten I/O)
2025-06-13 07:16:49 - [app] app - [INFO] INFO - Admin-Berechtigungen beim Start korrigiert: 0 erstellt, 0 aktualisiert
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [STARTUP] 🚀 Starte MYP DEVELOPMENT-Umgebung
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [STARTUP] 🏢 Mercedes-Benz TBA Marienfelde
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [STARTUP] 🔒 Air-Gapped: True
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [STARTUP] Initialisiere Datenbank...
2025-06-13 07:16:49 - [app] app - [INFO] INFO - Datenbank mit Optimierungen initialisiert
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [STARTUP] ✅ Datenbank initialisiert
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [STARTUP] Prüfe Initial-Admin...
2025-06-13 07:16:49 - [app] app - [INFO] INFO - Admin-Benutzer admin (admin@mercedes-benz.com) existiert bereits. Passwort wurde zurückgesetzt.
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [STARTUP] ✅ Admin-Benutzer geprüft
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [STARTUP] Starte Queue Manager...
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [STARTUP] ✅ Queue Manager gestartet
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [STARTUP] Starte Job Scheduler...
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [STARTUP] ✅ Job Scheduler gestartet
2025-06-13 07:16:49 - [app] app - [INFO] INFO - [STARTUP] 🌐 Server startet auf http://0.0.0.0:5000
2025-06-13 07:16:51 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: backend/database/myp.db
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [CONFIG] Erkannte Umgebung: development
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [CONFIG] Production-Modus: False
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [CONFIG] Verwende Development-Konfiguration
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [DEVELOPMENT] Aktiviere Development-Konfiguration
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ MYP Development Environment Konfiguration aktiviert
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ Environment: Development/Testing
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ Debug Mode: True
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ SQL Echo: True
2025-06-13 07:16:52 - [app] app - [INFO] INFO - SQLite für Raspberry Pi optimiert (reduzierte Cache-Größe, SD-Karten I/O)
2025-06-13 07:16:52 - [app] app - [INFO] INFO - Admin-Berechtigungen beim Start korrigiert: 0 erstellt, 0 aktualisiert
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [STARTUP] 🚀 Starte MYP DEVELOPMENT-Umgebung
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [STARTUP] 🏢 Mercedes-Benz TBA Marienfelde
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [STARTUP] 🔒 Air-Gapped: True
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [STARTUP] Initialisiere Datenbank...
2025-06-13 07:16:52 - [app] app - [INFO] INFO - Datenbank mit Optimierungen initialisiert
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [STARTUP] ✅ Datenbank initialisiert
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [STARTUP] Prüfe Initial-Admin...
2025-06-13 07:16:52 - [app] app - [INFO] INFO - Admin-Benutzer admin (admin@mercedes-benz.com) existiert bereits. Passwort wurde zurückgesetzt.
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [STARTUP] ✅ Admin-Benutzer geprüft
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [STARTUP] Starte Queue Manager...
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [STARTUP] ✅ Queue Manager gestartet
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [STARTUP] Starte Job Scheduler...
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [STARTUP] ✅ Job Scheduler gestartet
2025-06-13 07:16:52 - [app] app - [INFO] INFO - [STARTUP] 🌐 Server startet auf http://0.0.0.0:5000
2025-06-13 07:16:52 - [app] app - [INFO] INFO - Locating template 'dashboard.html':
1: trying loader of application '__main__'
class: jinja2.loaders.FileSystemLoader
encoding: 'utf-8'
followlinks: False
searchpath:
- C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\templates
-> found ('C:\\Users\\TTOMCZA.EMEA\\Dev\\Projektarbeit-MYP\\backend\\templates\\dashboard.html')
2025-06-13 07:16:52 - [app] app - [INFO] INFO - Locating template 'base.html':
1: trying loader of application '__main__'
class: jinja2.loaders.FileSystemLoader
encoding: 'utf-8'
followlinks: False
searchpath:
- C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\templates
-> found ('C:\\Users\\TTOMCZA.EMEA\\Dev\\Projektarbeit-MYP\\backend\\templates\\base.html')
2025-06-13 07:16:52 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:16:57 - [app] app - [DEBUG] DEBUG - Request: GET /requests/overview
2025-06-13 07:16:57 - [app] app - [INFO] INFO - Locating template 'guest_requests_overview.html':
1: trying loader of application '__main__'
class: jinja2.loaders.FileSystemLoader
encoding: 'utf-8'
followlinks: False
searchpath:
- C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\templates
-> found ('C:\\Users\\TTOMCZA.EMEA\\Dev\\Projektarbeit-MYP\\backend\\templates\\guest_requests_overview.html')
2025-06-13 07:16:58 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:17:07 - [app] app - [DEBUG] DEBUG - Request: GET /user/settings
2025-06-13 07:17:07 - [app] app - [INFO] INFO - Locating template 'settings.html':
1: trying loader of application '__main__'
class: jinja2.loaders.FileSystemLoader
encoding: 'utf-8'
followlinks: False
searchpath:
- C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\templates
-> found ('C:\\Users\\TTOMCZA.EMEA\\Dev\\Projektarbeit-MYP\\backend\\templates\\settings.html')
2025-06-13 07:17:07 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:17:12 - [app] app - [DEBUG] DEBUG - Request: GET /user/settings
2025-06-13 07:17:12 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:17:21 - [app] app - [DEBUG] DEBUG - Request: GET /admin/
2025-06-13 07:17:21 - [app] app - [INFO] INFO - Locating template 'admin.html':
1: trying loader of application '__main__'
class: jinja2.loaders.FileSystemLoader
encoding: 'utf-8'
followlinks: False
searchpath:
- C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\templates
-> found ('C:\\Users\\TTOMCZA.EMEA\\Dev\\Projektarbeit-MYP\\backend\\templates\\admin.html')
2025-06-13 07:17:21 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:17:21 - [app] app - [DEBUG] DEBUG - Request: GET /api/stats
2025-06-13 07:17:21 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:17:21 - [app] app - [DEBUG] DEBUG - Request: GET /api/admin/system-health
2025-06-13 07:17:21 - [app] app - [ERROR] ERROR - Datenbank-Transaktion fehlgeschlagen: Textual SQL expression 'SELECT 1' should be explicitly declared as text('SELECT 1')
2025-06-13 07:17:21 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:17:23 - [app] app - [DEBUG] DEBUG - Request: GET /admin/guest-requests
2025-06-13 07:17:23 - [app] app - [INFO] INFO - Locating template 'admin_guest_requests.html':
1: trying loader of application '__main__'
class: jinja2.loaders.FileSystemLoader
encoding: 'utf-8'
followlinks: False
searchpath:
- C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\templates
-> found ('C:\\Users\\TTOMCZA.EMEA\\Dev\\Projektarbeit-MYP\\backend\\templates\\admin_guest_requests.html')
2025-06-13 07:17:23 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:17:24 - [app] app - [DEBUG] DEBUG - Request: GET /api/admin/requests
2025-06-13 07:17:24 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:17:33 - [app] app - [DEBUG] DEBUG - Request: POST /api/requests/2/approve
2025-06-13 07:17:33 - [app] app - [INFO] INFO - OTP generiert für Guest Request 2
2025-06-13 07:17:33 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:17:33 - [app] app - [DEBUG] DEBUG - Request: GET /api/admin/requests
2025-06-13 07:17:33 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:17:41 - [app] app - [DEBUG] DEBUG - Request: POST /api/guest-requests/1/reject
2025-06-13 07:17:41 - [app] app - [INFO] INFO - Not Found (404): http://127.0.0.1:5000/api/guest-requests/1/reject
2025-06-13 07:17:41 - [app] app - [DEBUG] DEBUG - Response: 404
2025-06-13 07:17:54 - [app] app - [DEBUG] DEBUG - Request: GET /api/admin/requests
2025-06-13 07:17:54 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:18:15 - [app] app - [DEBUG] DEBUG - Request: GET /jobs
2025-06-13 07:18:15 - [app] app - [INFO] INFO - Locating template 'jobs.html':
1: trying loader of application '__main__'
class: jinja2.loaders.FileSystemLoader
encoding: 'utf-8'
followlinks: False
searchpath:
- C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\templates
-> found ('C:\\Users\\TTOMCZA.EMEA\\Dev\\Projektarbeit-MYP\\backend\\templates\\jobs.html')
2025-06-13 07:18:15 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:18:15 - [app] app - [DEBUG] DEBUG - Request: GET /api/jobs
2025-06-13 07:18:15 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:18:15 - [app] app - [DEBUG] DEBUG - Request: GET /api/printers
2025-06-13 07:18:15 - [app] app - [INFO] INFO - ✅ API: 6 Drucker abgerufen (include_inactive=True)
2025-06-13 07:18:15 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:18:15 - [app] app - [INFO] INFO - [SHUTDOWN] 🧹 Cleanup wird ausgeführt...
2025-06-13 07:18:15 - [app] app - [INFO] INFO - [SHUTDOWN] ✅ Queue Manager gestoppt
2025-06-13 07:18:15 - [app] app - [ERROR] ERROR - [SHUTDOWN] ❌ Cleanup-Fehler: 'BackgroundTaskScheduler' object has no attribute 'shutdown'
2025-06-13 07:18:17 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: backend/database/myp.db
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [CONFIG] Erkannte Umgebung: development
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [CONFIG] Production-Modus: False
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [CONFIG] Verwende Development-Konfiguration
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [DEVELOPMENT] Aktiviere Development-Konfiguration
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ MYP Development Environment Konfiguration aktiviert
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ Environment: Development/Testing
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ Debug Mode: True
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [DEVELOPMENT] ✅ SQL Echo: True
2025-06-13 07:18:18 - [app] app - [INFO] INFO - SQLite für Raspberry Pi optimiert (reduzierte Cache-Größe, SD-Karten I/O)
2025-06-13 07:18:18 - [app] app - [INFO] INFO - Admin-Berechtigungen beim Start korrigiert: 0 erstellt, 0 aktualisiert
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [STARTUP] 🚀 Starte MYP DEVELOPMENT-Umgebung
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [STARTUP] 🏢 Mercedes-Benz TBA Marienfelde
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [STARTUP] 🔒 Air-Gapped: True
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [STARTUP] Initialisiere Datenbank...
2025-06-13 07:18:18 - [app] app - [INFO] INFO - Datenbank mit Optimierungen initialisiert
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [STARTUP] ✅ Datenbank initialisiert
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [STARTUP] Prüfe Initial-Admin...
2025-06-13 07:18:18 - [app] app - [INFO] INFO - Admin-Benutzer admin (admin@mercedes-benz.com) existiert bereits. Passwort wurde zurückgesetzt.
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [STARTUP] ✅ Admin-Benutzer geprüft
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [STARTUP] Starte Queue Manager...
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [STARTUP] ✅ Queue Manager gestartet
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [STARTUP] Starte Job Scheduler...
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [STARTUP] ✅ Job Scheduler gestartet
2025-06-13 07:18:18 - [app] app - [INFO] INFO - [STARTUP] 🌐 Server startet auf http://0.0.0.0:5000
2025-06-13 07:18:33 - [app] app - [INFO] INFO - Locating template 'guest_request.html':
1: trying loader of application '__main__'
class: jinja2.loaders.FileSystemLoader
encoding: 'utf-8'
followlinks: False
searchpath:
- C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\templates
-> found ('C:\\Users\\TTOMCZA.EMEA\\Dev\\Projektarbeit-MYP\\backend\\templates\\guest_request.html')
2025-06-13 07:18:33 - [app] app - [INFO] INFO - Locating template 'base.html':
1: trying loader of application '__main__'
class: jinja2.loaders.FileSystemLoader
encoding: 'utf-8'
followlinks: False
searchpath:
- C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\templates
-> found ('C:\\Users\\TTOMCZA.EMEA\\Dev\\Projektarbeit-MYP\\backend\\templates\\base.html')
2025-06-13 07:18:33 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:18:36 - [app] app - [DEBUG] DEBUG - Request: GET /requests/overview
2025-06-13 07:18:36 - [app] app - [INFO] INFO - Locating template 'guest_requests_overview.html':
1: trying loader of application '__main__'
class: jinja2.loaders.FileSystemLoader
encoding: 'utf-8'
followlinks: False
searchpath:
- C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\templates
-> found ('C:\\Users\\TTOMCZA.EMEA\\Dev\\Projektarbeit-MYP\\backend\\templates\\guest_requests_overview.html')
2025-06-13 07:18:36 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:18:47 - [app] app - [DEBUG] DEBUG - Request: GET /printers
2025-06-13 07:18:47 - [app] app - [INFO] INFO - Locating template 'printers.html':
1: trying loader of application '__main__'
class: jinja2.loaders.FileSystemLoader
encoding: 'utf-8'
followlinks: False
searchpath:
- C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\templates
-> found ('C:\\Users\\TTOMCZA.EMEA\\Dev\\Projektarbeit-MYP\\backend\\templates\\printers.html')
2025-06-13 07:18:47 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:18:47 - [app] app - [DEBUG] DEBUG - Request: GET /api/printers
2025-06-13 07:18:47 - [app] app - [INFO] INFO - ✅ API: 6 Drucker abgerufen (include_inactive=True)
2025-06-13 07:18:47 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:18:47 - [app] app - [DEBUG] DEBUG - Request: GET /api/printers
2025-06-13 07:18:47 - [app] app - [INFO] INFO - ✅ API: 6 Drucker abgerufen (include_inactive=True)
2025-06-13 07:18:47 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:18:48 - [app] app - [DEBUG] DEBUG - Request: GET /jobs
2025-06-13 07:18:48 - [app] app - [INFO] INFO - Locating template 'jobs.html':
1: trying loader of application '__main__'
class: jinja2.loaders.FileSystemLoader
encoding: 'utf-8'
followlinks: False
searchpath:
- C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\templates
-> found ('C:\\Users\\TTOMCZA.EMEA\\Dev\\Projektarbeit-MYP\\backend\\templates\\jobs.html')
2025-06-13 07:18:48 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:18:48 - [app] app - [DEBUG] DEBUG - Request: GET /api/jobs
2025-06-13 07:18:48 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:18:48 - [app] app - [DEBUG] DEBUG - Request: GET /api/printers
2025-06-13 07:18:48 - [app] app - [INFO] INFO - ✅ API: 6 Drucker abgerufen (include_inactive=True)
2025-06-13 07:18:48 - [app] app - [DEBUG] DEBUG - Response: 200
2025-06-13 07:19:18 - [app] app - [DEBUG] DEBUG - Request: GET /api/jobs
2025-06-13 07:19:18 - [app] app - [DEBUG] DEBUG - Response: 200

View File

@ -174,3 +174,9 @@
2025-06-13 07:12:33 - [core_system] core_system - [INFO] INFO - 📊 Massive Konsolidierung: 6 Dateien → 1 Datei (88% Reduktion) 2025-06-13 07:12:33 - [core_system] core_system - [INFO] INFO - 📊 Massive Konsolidierung: 6 Dateien → 1 Datei (88% Reduktion)
2025-06-13 07:12:36 - [core_system] core_system - [INFO] INFO - ✅ Core System Management Module erfolgreich initialisiert 2025-06-13 07:12:36 - [core_system] core_system - [INFO] INFO - ✅ Core System Management Module erfolgreich initialisiert
2025-06-13 07:12:36 - [core_system] core_system - [INFO] INFO - 📊 Massive Konsolidierung: 6 Dateien → 1 Datei (88% Reduktion) 2025-06-13 07:12:36 - [core_system] core_system - [INFO] INFO - 📊 Massive Konsolidierung: 6 Dateien → 1 Datei (88% Reduktion)
2025-06-13 07:16:48 - [core_system] core_system - [INFO] INFO - ✅ Core System Management Module erfolgreich initialisiert
2025-06-13 07:16:48 - [core_system] core_system - [INFO] INFO - 📊 Massive Konsolidierung: 6 Dateien → 1 Datei (88% Reduktion)
2025-06-13 07:16:50 - [core_system] core_system - [INFO] INFO - ✅ Core System Management Module erfolgreich initialisiert
2025-06-13 07:16:50 - [core_system] core_system - [INFO] INFO - 📊 Massive Konsolidierung: 6 Dateien → 1 Datei (88% Reduktion)
2025-06-13 07:18:17 - [core_system] core_system - [INFO] INFO - ✅ Core System Management Module erfolgreich initialisiert
2025-06-13 07:18:17 - [core_system] core_system - [INFO] INFO - 📊 Massive Konsolidierung: 6 Dateien → 1 Datei (88% Reduktion)

View File

@ -198,3 +198,9 @@
2025-06-13 07:12:33 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion) 2025-06-13 07:12:33 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-13 07:12:36 - [data_management] data_management - [INFO] INFO - ✅ Data Management Module initialisiert 2025-06-13 07:12:36 - [data_management] data_management - [INFO] INFO - ✅ Data Management Module initialisiert
2025-06-13 07:12:36 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion) 2025-06-13 07:12:36 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-13 07:16:48 - [data_management] data_management - [INFO] INFO - ✅ Data Management Module initialisiert
2025-06-13 07:16:48 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-13 07:16:51 - [data_management] data_management - [INFO] INFO - ✅ Data Management Module initialisiert
2025-06-13 07:16:51 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-13 07:18:17 - [data_management] data_management - [INFO] INFO - ✅ Data Management Module initialisiert
2025-06-13 07:18:17 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)

View File

@ -40,3 +40,6 @@
2025-06-13 07:13:12 - [energy_monitoring] energy_monitoring - [INFO] INFO - ✅ Dashboard-Daten erfolgreich erstellt: 0 Geräte online 2025-06-13 07:13:12 - [energy_monitoring] energy_monitoring - [INFO] INFO - ✅ Dashboard-Daten erfolgreich erstellt: 0 Geräte online
2025-06-13 07:13:12 - [energy_monitoring] energy_monitoring - [INFO] INFO - [OK] API-Energiemonitoring-Dashboard 'api_energy_dashboard' erfolgreich in 7.50ms 2025-06-13 07:13:12 - [energy_monitoring] energy_monitoring - [INFO] INFO - [OK] API-Energiemonitoring-Dashboard 'api_energy_dashboard' erfolgreich in 7.50ms
2025-06-13 07:13:12 - [energy_monitoring] energy_monitoring - [INFO] INFO - [OK] API-Live-Energiedaten 'api_live_energy_data' erfolgreich in 5.26ms 2025-06-13 07:13:12 - [energy_monitoring] energy_monitoring - [INFO] INFO - [OK] API-Live-Energiedaten 'api_live_energy_data' erfolgreich in 5.26ms
2025-06-13 07:16:49 - [energy_monitoring] energy_monitoring - [INFO] INFO - ✅ Energiemonitoring-Blueprint initialisiert
2025-06-13 07:16:52 - [energy_monitoring] energy_monitoring - [INFO] INFO - ✅ Energiemonitoring-Blueprint initialisiert
2025-06-13 07:18:18 - [energy_monitoring] energy_monitoring - [INFO] INFO - ✅ Energiemonitoring-Blueprint initialisiert

View File

@ -34,3 +34,4 @@ WHERE user_permissions.can_approve_jobs = 1]
[SQL: INSERT INTO notifications (user_id, title, message, type, payload, created_at, is_read, read_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)] [SQL: INSERT INTO notifications (user_id, title, message, type, payload, created_at, is_read, read_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)]
[parameters: (1, None, None, 'guest_request', '{"request_id": 2, "name": "Till Tomczaktet", "created_at": "2025-06-13T07:13:42.749201", "status": "pending"}', '2025-06-13 07:13:42.954291', 0, None)] [parameters: (1, None, None, 'guest_request', '{"request_id": 2, "name": "Till Tomczaktet", "created_at": "2025-06-13T07:13:42.749201", "status": "pending"}', '2025-06-13 07:13:42.954291', 0, None)]
(Background on this error at: https://sqlalche.me/e/20/e3q8) (Background on this error at: https://sqlalche.me/e/20/e3q8)
2025-06-13 07:17:33 - [guest] guest - [INFO] INFO - Gastanfrage 2 genehmigt von Admin 1 (admin)

View File

@ -514,3 +514,15 @@
2025-06-13 07:13:12 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ Energiestatistiken erfolgreich gesammelt: 0/6 Geräte online 2025-06-13 07:13:12 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ Energiestatistiken erfolgreich gesammelt: 0/6 Geräte online
2025-06-13 07:13:12 - [hardware_integration] hardware_integration - [INFO] INFO - 📊 Gesamtverbrauch: 0.0W aktuell, 0.0Wh heute 2025-06-13 07:13:12 - [hardware_integration] hardware_integration - [INFO] INFO - 📊 Gesamtverbrauch: 0.0W aktuell, 0.0Wh heute
2025-06-13 07:13:12 - [hardware_integration] hardware_integration - [INFO] INFO - 📊 Gesamtverbrauch: 0.0W aktuell, 0.0Wh heute 2025-06-13 07:13:12 - [hardware_integration] hardware_integration - [INFO] INFO - 📊 Gesamtverbrauch: 0.0W aktuell, 0.0Wh heute
2025-06-13 07:16:48 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ PyP100 (TP-Link Tapo) verfügbar
2025-06-13 07:16:48 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ Printer Monitor initialisiert
2025-06-13 07:16:48 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ Hardware Integration Module initialisiert
2025-06-13 07:16:48 - [hardware_integration] hardware_integration - [INFO] INFO - 📊 Massive Konsolidierung: 2 Dateien → 1 Datei (50% Reduktion)
2025-06-13 07:16:51 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ PyP100 (TP-Link Tapo) verfügbar
2025-06-13 07:16:51 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ Printer Monitor initialisiert
2025-06-13 07:16:51 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ Hardware Integration Module initialisiert
2025-06-13 07:16:51 - [hardware_integration] hardware_integration - [INFO] INFO - 📊 Massive Konsolidierung: 2 Dateien → 1 Datei (50% Reduktion)
2025-06-13 07:18:17 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ PyP100 (TP-Link Tapo) verfügbar
2025-06-13 07:18:17 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ Printer Monitor initialisiert
2025-06-13 07:18:17 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ Hardware Integration Module initialisiert
2025-06-13 07:18:17 - [hardware_integration] hardware_integration - [INFO] INFO - 📊 Massive Konsolidierung: 2 Dateien → 1 Datei (50% Reduktion)

View File

@ -378,3 +378,15 @@
2025-06-13 07:12:37 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestartet (Legacy-Kompatibilität) 2025-06-13 07:12:37 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestartet (Legacy-Kompatibilität)
2025-06-13 07:14:55 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestoppt (Legacy-Kompatibilität) 2025-06-13 07:14:55 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestoppt (Legacy-Kompatibilität)
2025-06-13 07:14:55 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestoppt (Legacy-Kompatibilität) 2025-06-13 07:14:55 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestoppt (Legacy-Kompatibilität)
2025-06-13 07:16:48 - [job_queue_system] job_queue_system - [INFO] INFO - ✅ Job & Queue System Module initialisiert
2025-06-13 07:16:48 - [job_queue_system] job_queue_system - [INFO] INFO - 📊 MASSIVE Konsolidierung: 4 Dateien → 1 Datei (75% Reduktion)
2025-06-13 07:16:49 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestartet (Legacy-Kompatibilität)
2025-06-13 07:16:51 - [job_queue_system] job_queue_system - [INFO] INFO - ✅ Job & Queue System Module initialisiert
2025-06-13 07:16:51 - [job_queue_system] job_queue_system - [INFO] INFO - 📊 MASSIVE Konsolidierung: 4 Dateien → 1 Datei (75% Reduktion)
2025-06-13 07:16:52 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestartet (Legacy-Kompatibilität)
2025-06-13 07:18:15 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestoppt (Legacy-Kompatibilität)
2025-06-13 07:18:17 - [job_queue_system] job_queue_system - [INFO] INFO - ✅ Job & Queue System Module initialisiert
2025-06-13 07:18:17 - [job_queue_system] job_queue_system - [INFO] INFO - 📊 MASSIVE Konsolidierung: 4 Dateien → 1 Datei (75% Reduktion)
2025-06-13 07:18:18 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestartet (Legacy-Kompatibilität)
2025-06-13 07:19:47 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestoppt (Legacy-Kompatibilität)
2025-06-13 07:19:47 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestoppt (Legacy-Kompatibilität)

View File

@ -567,3 +567,9 @@ IndexError: tuple index out of range
2025-06-12 21:12:23 - [jobs] jobs - [INFO] INFO - ✅ Jobs erfolgreich abgerufen: 0 von 0 (Seite 1) 2025-06-12 21:12:23 - [jobs] jobs - [INFO] INFO - ✅ Jobs erfolgreich abgerufen: 0 von 0 (Seite 1)
2025-06-13 07:12:54 - [jobs] jobs - [INFO] INFO - 📋 Jobs-Abfrage gestartet von Benutzer 1 (Admin: True) 2025-06-13 07:12:54 - [jobs] jobs - [INFO] INFO - 📋 Jobs-Abfrage gestartet von Benutzer 1 (Admin: True)
2025-06-13 07:12:54 - [jobs] jobs - [INFO] INFO - ✅ Jobs erfolgreich abgerufen: 0 von 0 (Seite 1) 2025-06-13 07:12:54 - [jobs] jobs - [INFO] INFO - ✅ Jobs erfolgreich abgerufen: 0 von 0 (Seite 1)
2025-06-13 07:18:15 - [jobs] jobs - [INFO] INFO - 📋 Jobs-Abfrage gestartet von Benutzer 1 (Admin: True)
2025-06-13 07:18:15 - [jobs] jobs - [INFO] INFO - ✅ Jobs erfolgreich abgerufen: 1 von 1 (Seite 1)
2025-06-13 07:18:48 - [jobs] jobs - [INFO] INFO - 📋 Jobs-Abfrage gestartet von Benutzer 1 (Admin: True)
2025-06-13 07:18:48 - [jobs] jobs - [INFO] INFO - ✅ Jobs erfolgreich abgerufen: 1 von 1 (Seite 1)
2025-06-13 07:19:18 - [jobs] jobs - [INFO] INFO - 📋 Jobs-Abfrage gestartet von Benutzer 1 (Admin: True)
2025-06-13 07:19:18 - [jobs] jobs - [INFO] INFO - ✅ Jobs erfolgreich abgerufen: 1 von 1 (Seite 1)

View File

@ -198,3 +198,9 @@
2025-06-13 07:12:34 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - 📊 MASSIVE Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion) 2025-06-13 07:12:34 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - 📊 MASSIVE Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-13 07:12:37 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - ✅ Monitoring & Analytics Module initialisiert 2025-06-13 07:12:37 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - ✅ Monitoring & Analytics Module initialisiert
2025-06-13 07:12:37 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - 📊 MASSIVE Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion) 2025-06-13 07:12:37 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - 📊 MASSIVE Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-13 07:16:49 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - ✅ Monitoring & Analytics Module initialisiert
2025-06-13 07:16:49 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - 📊 MASSIVE Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-13 07:16:52 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - ✅ Monitoring & Analytics Module initialisiert
2025-06-13 07:16:52 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - 📊 MASSIVE Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-13 07:18:18 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - ✅ Monitoring & Analytics Module initialisiert
2025-06-13 07:18:18 - [monitoring_analytics] monitoring_analytics - [INFO] INFO - 📊 MASSIVE Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)

View File

@ -1,3 +1,10 @@
2025-06-13 07:12:34 - [permissions] permissions - [INFO] INFO - UserPermission für Admin admin (ID: 1) erstellt 2025-06-13 07:12:34 - [permissions] permissions - [INFO] INFO - UserPermission für Admin admin (ID: 1) erstellt
2025-06-13 07:12:34 - [permissions] permissions - [INFO] INFO - Admin-Berechtigungen korrigiert: 1 erstellt, 0 aktualisiert 2025-06-13 07:12:34 - [permissions] permissions - [INFO] INFO - Admin-Berechtigungen korrigiert: 1 erstellt, 0 aktualisiert
2025-06-13 07:12:37 - [permissions] permissions - [INFO] INFO - Admin-Berechtigungen korrigiert: 0 erstellt, 0 aktualisiert 2025-06-13 07:12:37 - [permissions] permissions - [INFO] INFO - Admin-Berechtigungen korrigiert: 0 erstellt, 0 aktualisiert
2025-06-13 07:16:49 - [permissions] permissions - [INFO] INFO - Admin-Berechtigungen korrigiert: 0 erstellt, 0 aktualisiert
2025-06-13 07:16:52 - [permissions] permissions - [INFO] INFO - Admin-Berechtigungen korrigiert: 0 erstellt, 0 aktualisiert
2025-06-13 07:17:24 - [permissions] permissions - [INFO] INFO - UserPermission für Admin-Benutzer 1 aktualisiert
2025-06-13 07:17:33 - [permissions] permissions - [INFO] INFO - UserPermission für Admin-Benutzer 1 aktualisiert
2025-06-13 07:17:33 - [permissions] permissions - [INFO] INFO - UserPermission für Admin-Benutzer 1 aktualisiert
2025-06-13 07:17:54 - [permissions] permissions - [INFO] INFO - UserPermission für Admin-Benutzer 1 aktualisiert
2025-06-13 07:18:18 - [permissions] permissions - [INFO] INFO - Admin-Berechtigungen korrigiert: 0 erstellt, 0 aktualisiert

View File

@ -278,3 +278,12 @@
2025-06-13 07:12:36 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True 2025-06-13 07:12:36 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-13 07:12:37 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet 2025-06-13 07:12:37 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
2025-06-13 07:12:37 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet 2025-06-13 07:12:37 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet
2025-06-13 07:16:48 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-13 07:16:49 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
2025-06-13 07:16:49 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet
2025-06-13 07:16:51 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-13 07:16:52 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
2025-06-13 07:16:52 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet
2025-06-13 07:18:17 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
2025-06-13 07:18:18 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
2025-06-13 07:18:18 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet

View File

@ -299,3 +299,12 @@
2025-06-13 07:12:36 - [security_suite] security_suite - [INFO] INFO - ✅ Security Suite Module initialisiert 2025-06-13 07:12:36 - [security_suite] security_suite - [INFO] INFO - ✅ Security Suite Module initialisiert
2025-06-13 07:12:36 - [security_suite] security_suite - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion) 2025-06-13 07:12:36 - [security_suite] security_suite - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-13 07:12:37 - [security_suite] security_suite - [INFO] INFO - 🔒 Security Suite initialisiert 2025-06-13 07:12:37 - [security_suite] security_suite - [INFO] INFO - 🔒 Security Suite initialisiert
2025-06-13 07:16:48 - [security_suite] security_suite - [INFO] INFO - ✅ Security Suite Module initialisiert
2025-06-13 07:16:48 - [security_suite] security_suite - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-13 07:16:49 - [security_suite] security_suite - [INFO] INFO - 🔒 Security Suite initialisiert
2025-06-13 07:16:51 - [security_suite] security_suite - [INFO] INFO - ✅ Security Suite Module initialisiert
2025-06-13 07:16:51 - [security_suite] security_suite - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-13 07:16:52 - [security_suite] security_suite - [INFO] INFO - 🔒 Security Suite initialisiert
2025-06-13 07:18:17 - [security_suite] security_suite - [INFO] INFO - ✅ Security Suite Module initialisiert
2025-06-13 07:18:17 - [security_suite] security_suite - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
2025-06-13 07:18:18 - [security_suite] security_suite - [INFO] INFO - 🔒 Security Suite initialisiert

View File

@ -874,3 +874,30 @@
2025-06-13 07:12:37 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert 2025-06-13 07:12:37 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert
2025-06-13 07:12:37 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert 2025-06-13 07:12:37 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert
2025-06-13 07:12:37 - [startup] startup - [INFO] INFO - ================================================== 2025-06-13 07:12:37 - [startup] startup - [INFO] INFO - ==================================================
2025-06-13 07:16:49 - [startup] startup - [INFO] INFO - ==================================================
2025-06-13 07:16:49 - [startup] startup - [INFO] INFO - [START] MYP Platform Backend wird gestartet...
2025-06-13 07:16:49 - [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-13 07:16:49 - [startup] startup - [INFO] INFO - 💻 Betriebssystem: nt (win32)
2025-06-13 07:16:49 - [startup] startup - [INFO] INFO - 📁 Arbeitsverzeichnis: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend
2025-06-13 07:16:49 - [startup] startup - [INFO] INFO - ⏰ Startzeit: 2025-06-13T07:16:49.516029
2025-06-13 07:16:49 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert
2025-06-13 07:16:49 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert
2025-06-13 07:16:49 - [startup] startup - [INFO] INFO - ==================================================
2025-06-13 07:16:52 - [startup] startup - [INFO] INFO - ==================================================
2025-06-13 07:16:52 - [startup] startup - [INFO] INFO - [START] MYP Platform Backend wird gestartet...
2025-06-13 07:16:52 - [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-13 07:16:52 - [startup] startup - [INFO] INFO - 💻 Betriebssystem: nt (win32)
2025-06-13 07:16:52 - [startup] startup - [INFO] INFO - 📁 Arbeitsverzeichnis: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend
2025-06-13 07:16:52 - [startup] startup - [INFO] INFO - ⏰ Startzeit: 2025-06-13T07:16:52.168582
2025-06-13 07:16:52 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert
2025-06-13 07:16:52 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert
2025-06-13 07:16:52 - [startup] startup - [INFO] INFO - ==================================================
2025-06-13 07:18:18 - [startup] startup - [INFO] INFO - ==================================================
2025-06-13 07:18:18 - [startup] startup - [INFO] INFO - [START] MYP Platform Backend wird gestartet...
2025-06-13 07:18:18 - [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-13 07:18:18 - [startup] startup - [INFO] INFO - 💻 Betriebssystem: nt (win32)
2025-06-13 07:18:18 - [startup] startup - [INFO] INFO - 📁 Arbeitsverzeichnis: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend
2025-06-13 07:18:18 - [startup] startup - [INFO] INFO - ⏰ Startzeit: 2025-06-13T07:18:18.239085
2025-06-13 07:18:18 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert
2025-06-13 07:18:18 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert
2025-06-13 07:18:18 - [startup] startup - [INFO] INFO - ==================================================

View File

@ -100,3 +100,6 @@
2025-06-13 06:57:17 - [tapo_controller] tapo_controller - [INFO] INFO - ✅ tapo controller initialisiert 2025-06-13 06:57:17 - [tapo_controller] tapo_controller - [INFO] INFO - ✅ tapo controller initialisiert
2025-06-13 07:12:33 - [tapo_controller] tapo_controller - [INFO] INFO - ✅ tapo controller initialisiert 2025-06-13 07:12:33 - [tapo_controller] tapo_controller - [INFO] INFO - ✅ tapo controller initialisiert
2025-06-13 07:12:36 - [tapo_controller] tapo_controller - [INFO] INFO - ✅ tapo controller initialisiert 2025-06-13 07:12:36 - [tapo_controller] tapo_controller - [INFO] INFO - ✅ tapo controller initialisiert
2025-06-13 07:16:48 - [tapo_controller] tapo_controller - [INFO] INFO - ✅ tapo controller initialisiert
2025-06-13 07:16:51 - [tapo_controller] tapo_controller - [INFO] INFO - ✅ tapo controller initialisiert
2025-06-13 07:18:17 - [tapo_controller] tapo_controller - [INFO] INFO - ✅ tapo controller initialisiert

View File

@ -57,3 +57,6 @@
2025-06-13 06:57:17 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager initialisiert 2025-06-13 06:57:17 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager initialisiert
2025-06-13 07:12:33 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager initialisiert 2025-06-13 07:12:33 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager initialisiert
2025-06-13 07:12:36 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager initialisiert 2025-06-13 07:12:36 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager initialisiert
2025-06-13 07:16:48 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager initialisiert
2025-06-13 07:16:51 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager initialisiert
2025-06-13 07:18:17 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager initialisiert

View File

@ -84,3 +84,5 @@
2025-06-12 21:01:00 - [user] user - [INFO] INFO - User admin updated settings via API 2025-06-12 21:01:00 - [user] user - [INFO] INFO - User admin updated settings via API
2025-06-13 07:14:11 - [user] user - [INFO] INFO - User admin accessed settings page 2025-06-13 07:14:11 - [user] user - [INFO] INFO - User admin accessed settings page
2025-06-13 07:14:11 - [user] user - [INFO] INFO - User admin retrieved settings via API 2025-06-13 07:14:11 - [user] user - [INFO] INFO - User admin retrieved settings via API
2025-06-13 07:17:07 - [user] user - [INFO] INFO - User admin accessed settings page
2025-06-13 07:17:12 - [user] user - [INFO] INFO - User admin accessed settings page

View File

@ -208,3 +208,9 @@
2025-06-13 07:12:33 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion) 2025-06-13 07:12:33 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)
2025-06-13 07:12:36 - [utilities_collection] utilities_collection - [INFO] INFO - ✅ Utilities Collection initialisiert 2025-06-13 07:12:36 - [utilities_collection] utilities_collection - [INFO] INFO - ✅ Utilities Collection initialisiert
2025-06-13 07:12:36 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion) 2025-06-13 07:12:36 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)
2025-06-13 07:16:48 - [utilities_collection] utilities_collection - [INFO] INFO - ✅ Utilities Collection initialisiert
2025-06-13 07:16:48 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)
2025-06-13 07:16:50 - [utilities_collection] utilities_collection - [INFO] INFO - ✅ Utilities Collection initialisiert
2025-06-13 07:16:50 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)
2025-06-13 07:18:17 - [utilities_collection] utilities_collection - [INFO] INFO - ✅ Utilities Collection initialisiert
2025-06-13 07:18:17 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)

View File

@ -177,3 +177,9 @@
2025-06-13 07:12:33 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet 2025-06-13 07:12:33 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
2025-06-13 07:12:36 - [windows_fixes] windows_fixes - [INFO] INFO - 🔧 Wende Windows-spezifische Fixes an... 2025-06-13 07:12:36 - [windows_fixes] windows_fixes - [INFO] INFO - 🔧 Wende Windows-spezifische Fixes an...
2025-06-13 07:12:36 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet 2025-06-13 07:12:36 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
2025-06-13 07:16:48 - [windows_fixes] windows_fixes - [INFO] INFO - 🔧 Wende Windows-spezifische Fixes an...
2025-06-13 07:16:48 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
2025-06-13 07:16:50 - [windows_fixes] windows_fixes - [INFO] INFO - 🔧 Wende Windows-spezifische Fixes an...
2025-06-13 07:16:50 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet
2025-06-13 07:18:17 - [windows_fixes] windows_fixes - [INFO] INFO - 🔧 Wende Windows-spezifische Fixes an...
2025-06-13 07:18:17 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet

View File

@ -405,25 +405,31 @@ function getPriorityBadge(level) {
* CRUD-Operationen * CRUD-Operationen
*/ */
async function approveRequest(requestId) { async function approveRequest(requestId) {
if (!confirm('Möchten Sie diesen Gastauftrag wirklich genehmigen?')) return; const notes = prompt('Genehmigungsnotizen (optional):');
if (notes === null) return; // User clicked cancel
try { try {
showLoading(true); showLoading(true);
const url = `${API_BASE_URL}/api/guest-requests/${requestId}/approve`; const url = `${API_BASE_URL}/api/requests/${requestId}/approve`;
const response = await fetch(url, { const response = await fetch(url, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-CSRFToken': csrfToken 'X-CSRFToken': csrfToken
}, },
body: JSON.stringify({}) // Leeres JSON-Objekt senden body: JSON.stringify({
notes: notes || ''
})
}); });
const data = await response.json(); const data = await response.json();
if (data.success) { if (data.success) {
showNotification('✅ Gastauftrag erfolgreich genehmigt', 'success'); showNotification('✅ Gastauftrag erfolgreich genehmigt', 'success');
if (data.otp) {
showNotification(`🔑 OTP-Code für Gast: ${data.otp}`, 'info');
}
loadGuestRequests(); loadGuestRequests();
} else { } else {
throw new Error(data.message || 'Fehler beim Genehmigen'); throw new Error(data.message || 'Fehler beim Genehmigen');
@ -437,20 +443,23 @@ async function approveRequest(requestId) {
} }
async function rejectRequest(requestId) { async function rejectRequest(requestId) {
const reason = prompt('Grund für die Ablehnung:'); const reason = prompt('Grund für die Ablehnung (erforderlich):');
if (!reason) return; if (!reason || reason.trim() === '') {
showNotification('⚠️ Ablehnungsgrund ist erforderlich', 'warning');
return;
}
try { try {
showLoading(true); showLoading(true);
const url = `${API_BASE_URL}/api/guest-requests/${requestId}/reject`; const url = `${API_BASE_URL}/api/requests/${requestId}/deny`;
const response = await fetch(url, { const response = await fetch(url, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-CSRFToken': csrfToken 'X-CSRFToken': csrfToken
}, },
body: JSON.stringify({ reason }) body: JSON.stringify({ reason: reason.trim() })
}); });
const data = await response.json(); const data = await response.json();
@ -475,7 +484,7 @@ async function deleteRequest(requestId) {
try { try {
showLoading(true); showLoading(true);
const url = `${API_BASE_URL}/api/guest-requests/${requestId}`; const url = `${API_BASE_URL}/api/admin/requests/${requestId}`;
const response = await fetch(url, { const response = await fetch(url, {
method: 'DELETE', method: 'DELETE',
headers: { headers: {
@ -615,15 +624,28 @@ async function performBulkAction(action) {
closeBulkModal(); closeBulkModal();
const promises = selectedIds.map(async (id) => { const promises = selectedIds.map(async (id) => {
const url = `${API_BASE_URL}/api/guest-requests/${id}/${action}`; let url, method, body = null;
const method = action === 'delete' ? 'DELETE' : 'POST';
if (action === 'approve') {
url = `${API_BASE_URL}/api/requests/${id}/approve`;
method = 'POST';
body = JSON.stringify({ notes: '' });
} else if (action === 'reject') {
url = `${API_BASE_URL}/api/requests/${id}/deny`;
method = 'POST';
body = JSON.stringify({ reason: 'Bulk-Ablehnung durch Administrator' });
} else if (action === 'delete') {
url = `${API_BASE_URL}/api/admin/requests/${id}`;
method = 'DELETE';
}
return fetch(url, { return fetch(url, {
method, method,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-CSRFToken': csrfToken 'X-CSRFToken': csrfToken
} },
body: body
}); });
}); });

View File

@ -3,92 +3,29 @@
* Moderne Verwaltung von Gastaufträgen mit Live-Updates * Moderne Verwaltung von Gastaufträgen mit Live-Updates
*/ */
// Globale Variablen // Vereinfachte minimierte Version mit korrigierten API-URLs
const API_BASE_URL = document.location.origin;
let csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
let currentRequests = []; let currentRequests = [];
let filteredRequests = []; let filteredRequests = [];
let currentPage = 0;
let totalPages = 0;
let totalRequests = 0;
let refreshInterval = null;
let csrfToken = '';
// API Base URL Detection - Korrigierte Version für CSP-Kompatibilität
function detectApiBaseUrl() {
// Für lokale Entwicklung und CSP-Kompatibilität immer relative URLs verwenden
// Das verhindert CSP-Probleme mit connect-src
return ''; // Leerer String für relative URLs
}
const API_BASE_URL = detectApiBaseUrl();
// Initialisierung beim Laden der Seite
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// CSRF Token abrufen console.log('🎯 Admin Guest Requests Manager geladen');
csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '';
// Event Listeners initialisieren
initEventListeners(); initEventListeners();
// Daten initial laden
loadGuestRequests(); loadGuestRequests();
// Auto-Refresh starten
startAutoRefresh(); startAutoRefresh();
console.log('🎯 Admin Guest Requests Management geladen');
}); });
/**
* Event Listeners initialisieren
*/
function initEventListeners() { function initEventListeners() {
// Search Input document.getElementById('search-requests')?.addEventListener('input', handleSearch);
const searchInput = document.getElementById('search-requests'); document.getElementById('status-filter')?.addEventListener('change', handleFilterChange);
if (searchInput) { document.getElementById('sort-order')?.addEventListener('change', handleSortChange);
searchInput.addEventListener('input', debounce(handleSearch, 300)); document.getElementById('refresh-btn')?.addEventListener('click', loadGuestRequests);
document.getElementById('export-btn')?.addEventListener('click', handleExport);
document.getElementById('bulk-actions-btn')?.addEventListener('click', showBulkActionsModal);
document.getElementById('select-all')?.addEventListener('change', handleSelectAll);
} }
// Status Filter
const statusFilter = document.getElementById('status-filter');
if (statusFilter) {
statusFilter.addEventListener('change', handleFilterChange);
}
// Sort Order
const sortOrder = document.getElementById('sort-order');
if (sortOrder) {
sortOrder.addEventListener('change', handleSortChange);
}
// Action Buttons
const refreshBtn = document.getElementById('refresh-btn');
if (refreshBtn) {
refreshBtn.addEventListener('click', () => {
loadGuestRequests();
showNotification('🔄 Gastaufträge aktualisiert', 'info');
});
}
const exportBtn = document.getElementById('export-btn');
if (exportBtn) {
exportBtn.addEventListener('click', handleExport);
}
const bulkActionsBtn = document.getElementById('bulk-actions-btn');
if (bulkActionsBtn) {
bulkActionsBtn.addEventListener('click', showBulkActionsModal);
}
// Select All Checkbox
const selectAllCheckbox = document.getElementById('select-all');
if (selectAllCheckbox) {
selectAllCheckbox.addEventListener('change', handleSelectAll);
}
}
/**
* Gastaufträge von der API laden
*/
async function loadGuestRequests() { async function loadGuestRequests() {
try { try {
showLoading(true); showLoading(true);
@ -110,14 +47,8 @@ async function loadGuestRequests() {
if (data.success) { if (data.success) {
currentRequests = data.requests || []; currentRequests = data.requests || [];
totalRequests = data.total || 0;
// Statistiken aktualisieren
updateStats(data.stats || {}); updateStats(data.stats || {});
// Tabelle aktualisieren
applyFiltersAndSort(); applyFiltersAndSort();
console.log(`${currentRequests.length} Gastaufträge geladen`); console.log(`${currentRequests.length} Gastaufträge geladen`);
} else { } else {
throw new Error(data.message || 'Fehler beim Laden der Gastaufträge'); throw new Error(data.message || 'Fehler beim Laden der Gastaufträge');
@ -131,299 +62,30 @@ async function loadGuestRequests() {
} }
} }
/**
* Statistiken aktualisieren
*/
function updateStats(stats) {
const elements = {
'pending-count': stats.pending || 0,
'approved-count': stats.approved || 0,
'rejected-count': stats.rejected || 0,
'total-count': stats.total || 0
};
Object.entries(elements).forEach(([id, value]) => {
const element = document.getElementById(id);
if (element) {
animateCounter(element, value);
}
});
}
/**
* Counter mit Animation
*/
function animateCounter(element, targetValue) {
const currentValue = parseInt(element.textContent) || 0;
const difference = targetValue - currentValue;
const steps = 20;
const stepValue = difference / steps;
let step = 0;
const interval = setInterval(() => {
step++;
const value = Math.round(currentValue + (stepValue * step));
element.textContent = value;
if (step >= steps) {
clearInterval(interval);
element.textContent = targetValue;
}
}, 50);
}
/**
* Filter und Sortierung anwenden
*/
function applyFiltersAndSort() {
let requests = [...currentRequests];
// Status Filter
const statusFilter = document.getElementById('status-filter')?.value;
if (statusFilter && statusFilter !== 'all') {
requests = requests.filter(req => req.status === statusFilter);
}
// Such-Filter
const searchTerm = document.getElementById('search-requests')?.value.toLowerCase();
if (searchTerm) {
requests = requests.filter(req =>
req.name?.toLowerCase().includes(searchTerm) ||
req.email?.toLowerCase().includes(searchTerm) ||
req.file_name?.toLowerCase().includes(searchTerm) ||
req.reason?.toLowerCase().includes(searchTerm)
);
}
// Sortierung
const sortOrder = document.getElementById('sort-order')?.value;
requests.sort((a, b) => {
switch (sortOrder) {
case 'oldest':
return new Date(a.created_at) - new Date(b.created_at);
case 'priority':
return getPriorityValue(b) - getPriorityValue(a);
case 'newest':
default:
return new Date(b.created_at) - new Date(a.created_at);
}
});
filteredRequests = requests;
renderRequestsTable();
}
/**
* Prioritätswert für Sortierung berechnen
*/
function getPriorityValue(request) {
const now = new Date();
const created = new Date(request.created_at);
const hoursOld = (now - created) / (1000 * 60 * 60);
let priority = 0;
// Status-basierte Priorität
if (request.status === 'pending') priority += 10;
else if (request.status === 'approved') priority += 5;
// Alter-basierte Priorität
if (hoursOld > 24) priority += 5;
else if (hoursOld > 8) priority += 3;
else if (hoursOld > 2) priority += 1;
return priority;
}
/**
* Requests-Tabelle rendern
*/
function renderRequestsTable() {
const tableBody = document.getElementById('requests-table-body');
const emptyState = document.getElementById('empty-state');
if (!tableBody) return;
if (filteredRequests.length === 0) {
tableBody.innerHTML = '';
showEmptyState();
return;
}
hideEmptyState();
const requestsHtml = filteredRequests.map(request => createRequestRow(request)).join('');
tableBody.innerHTML = requestsHtml;
// Event Listeners für neue Rows hinzufügen
addRowEventListeners();
}
/**
* Request Row HTML erstellen
*/
function createRequestRow(request) {
const statusColor = getStatusColor(request.status);
const priorityLevel = getPriorityLevel(request);
const timeAgo = getTimeAgo(request.created_at);
return `
<tr class="hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors duration-200" data-request-id="${request.id}">
<td class="px-6 py-4">
<input type="checkbox" class="request-checkbox rounded border-slate-300 text-blue-600 focus:ring-blue-500"
value="${request.id}">
</td>
<td class="px-6 py-4">
<div class="flex items-center">
<div class="flex-shrink-0 h-10 w-10">
<div class="h-10 w-10 rounded-full bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center text-white font-semibold">
${request.name ? request.name[0].toUpperCase() : 'G'}
</div>
</div>
<div class="ml-4">
<div class="text-sm font-medium text-slate-900 dark:text-white">${escapeHtml(request.name || 'Unbekannt')}</div>
<div class="text-sm text-slate-500 dark:text-slate-400">${escapeHtml(request.email || 'Keine E-Mail')}</div>
</div>
</div>
</td>
<td class="px-6 py-4">
<div class="text-sm text-slate-900 dark:text-white font-medium">${escapeHtml(request.file_name || 'Keine Datei')}</div>
<div class="text-sm text-slate-500 dark:text-slate-400">
${request.duration_minutes ? `${request.duration_minutes} Min.` : 'Unbekannte Dauer'}
${request.copies ? `${request.copies} Kopien` : ''}
</div>
${request.reason ? `<div class="text-xs text-slate-400 dark:text-slate-500 mt-1 truncate max-w-xs">${escapeHtml(request.reason)}</div>` : ''}
</td>
<td class="px-6 py-4">
<span class="inline-flex items-center px-2 py-1 text-xs font-semibold rounded-full ${statusColor}">
<span class="w-2 h-2 mr-1 rounded-full ${getStatusDot(request.status)}"></span>
${getStatusText(request.status)}
</span>
</td>
<td class="px-6 py-4">
<div class="text-sm text-slate-900 dark:text-white">${timeAgo}</div>
<div class="text-xs text-slate-500 dark:text-slate-400">${formatDateTime(request.created_at)}</div>
</td>
<td class="px-6 py-4">
<div class="flex items-center">
${getPriorityBadge(priorityLevel)}
${request.is_urgent ? '<span class="ml-2 text-red-500 text-xs">🔥 Dringend</span>' : ''}
</div>
</td>
<td class="px-6 py-4">
<div class="flex items-center space-x-2">
<button onclick="showRequestDetail(${request.id})"
class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-300 transition-colors"
title="Details anzeigen">
<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="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
</button>
${request.status === 'pending' ? `
<button onclick="approveRequest(${request.id})"
class="text-green-600 hover:text-green-900 dark:text-green-400 dark:hover:text-green-300 transition-colors"
title="Genehmigen">
<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="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</button>
<button onclick="rejectRequest(${request.id})"
class="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 transition-colors"
title="Ablehnen">
<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="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</button>
` : ''}
<button onclick="deleteRequest(${request.id})"
class="text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300 transition-colors"
title="Löschen">
<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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
</svg>
</button>
</div>
</td>
</tr>
`;
}
/**
* Status-Helper-Funktionen
*/
function getStatusColor(status) {
const colors = {
'pending': 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300',
'approved': 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300',
'rejected': 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300',
'expired': 'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-300'
};
return colors[status] || 'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-300';
}
function getStatusDot(status) {
const dots = {
'pending': 'bg-yellow-400 dark:bg-yellow-300',
'approved': 'bg-green-400 dark:bg-green-300',
'rejected': 'bg-red-400 dark:bg-red-300',
'expired': 'bg-gray-400 dark:bg-gray-300'
};
return dots[status] || 'bg-gray-400 dark:bg-gray-300';
}
function getStatusText(status) {
const texts = {
'pending': 'Wartend',
'approved': 'Genehmigt',
'rejected': 'Abgelehnt',
'expired': 'Abgelaufen'
};
return texts[status] || status;
}
function getPriorityLevel(request) {
const priority = getPriorityValue(request);
if (priority >= 15) return 'high';
if (priority >= 8) return 'medium';
return 'low';
}
function getPriorityBadge(level) {
const badges = {
'high': '<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300">🔴 Hoch</span>',
'medium': '<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300">🟡 Mittel</span>',
'low': '<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300">🟢 Niedrig</span>'
};
return badges[level] || badges['low'];
}
/**
* CRUD-Operationen
*/
async function approveRequest(requestId) { async function approveRequest(requestId) {
if (!confirm('Möchten Sie diesen Gastauftrag wirklich genehmigen?')) return; const notes = prompt('Genehmigungsnotizen (optional):');
if (notes === null) return;
try { try {
showLoading(true); showLoading(true);
const url = `${API_BASE_URL}/api/guest-requests/${requestId}/approve`; const url = `${API_BASE_URL}/api/requests/${requestId}/approve`;
const response = await fetch(url, { const response = await fetch(url, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-CSRFToken': csrfToken 'X-CSRFToken': csrfToken
}, },
body: JSON.stringify({}) // Leeres JSON-Objekt senden body: JSON.stringify({ notes: notes || '' })
}); });
const data = await response.json(); const data = await response.json();
if (data.success) { if (data.success) {
showNotification('✅ Gastauftrag erfolgreich genehmigt', 'success'); showNotification('✅ Gastauftrag erfolgreich genehmigt', 'success');
if (data.otp) {
showNotification(`🔑 OTP-Code für Gast: ${data.otp}`, 'info');
}
loadGuestRequests(); loadGuestRequests();
} else { } else {
throw new Error(data.message || 'Fehler beim Genehmigen'); throw new Error(data.message || 'Fehler beim Genehmigen');
@ -437,20 +99,23 @@ async function approveRequest(requestId) {
} }
async function rejectRequest(requestId) { async function rejectRequest(requestId) {
const reason = prompt('Grund für die Ablehnung:'); const reason = prompt('Grund für die Ablehnung (erforderlich):');
if (!reason) return; if (!reason || reason.trim() === '') {
showNotification('⚠️ Ablehnungsgrund ist erforderlich', 'warning');
return;
}
try { try {
showLoading(true); showLoading(true);
const url = `${API_BASE_URL}/api/guest-requests/${requestId}/reject`; const url = `${API_BASE_URL}/api/requests/${requestId}/deny`;
const response = await fetch(url, { const response = await fetch(url, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-CSRFToken': csrfToken 'X-CSRFToken': csrfToken
}, },
body: JSON.stringify({ reason }) body: JSON.stringify({ reason: reason.trim() })
}); });
const data = await response.json(); const data = await response.json();
@ -475,7 +140,7 @@ async function deleteRequest(requestId) {
try { try {
showLoading(true); showLoading(true);
const url = `${API_BASE_URL}/api/guest-requests/${requestId}`; const url = `${API_BASE_URL}/api/admin/requests/${requestId}`;
const response = await fetch(url, { const response = await fetch(url, {
method: 'DELETE', method: 'DELETE',
headers: { headers: {
@ -500,376 +165,176 @@ async function deleteRequest(requestId) {
} }
} }
/** // Utility Functions
* Detail-Modal Funktionen function updateStats(stats) {
*/ document.getElementById('pending-count').textContent = stats.pending || 0;
function showRequestDetail(requestId) { document.getElementById('approved-count').textContent = stats.approved || 0;
const request = currentRequests.find(req => req.id === requestId); document.getElementById('rejected-count').textContent = stats.denied || 0;
if (!request) return; document.getElementById('total-count').textContent = stats.total || 0;
}
const modal = document.getElementById('detail-modal'); function applyFiltersAndSort() {
const content = document.getElementById('modal-content'); const searchTerm = document.getElementById('search-requests')?.value.toLowerCase() || '';
const statusFilter = document.getElementById('status-filter')?.value || 'all';
const sortOrder = document.getElementById('sort-order')?.value || 'newest';
content.innerHTML = ` let requests = [...currentRequests];
<div class="p-6 border-b border-gray-200 dark:border-gray-700">
<div class="flex justify-between items-center">
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Gastauftrag Details</h3>
<button onclick="closeDetailModal()" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
<svg class="w-6 h-6" 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>
</div>
<div class="p-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-4">
<h4 class="text-lg font-semibold text-gray-900 dark:text-white">Antragsteller</h4>
<div class="bg-slate-50 dark:bg-slate-700 rounded-lg p-4">
<p><strong>Name:</strong> ${escapeHtml(request.name || 'Unbekannt')}</p>
<p><strong>E-Mail:</strong> ${escapeHtml(request.email || 'Keine E-Mail')}</p>
<p><strong>Erstellt am:</strong> ${formatDateTime(request.created_at)}</p>
</div>
</div>
<div class="space-y-4"> if (searchTerm) {
<h4 class="text-lg font-semibold text-gray-900 dark:text-white">Auftrag Details</h4> requests = requests.filter(req =>
<div class="bg-slate-50 dark:bg-slate-700 rounded-lg p-4"> (req.name || '').toLowerCase().includes(searchTerm) ||
<p><strong>Datei:</strong> ${escapeHtml(request.file_name || 'Keine Datei')}</p> (req.email || '').toLowerCase().includes(searchTerm) ||
<p><strong>Dauer:</strong> ${request.duration_minutes || 'Unbekannt'} Minuten</p> (req.file_name || '').toLowerCase().includes(searchTerm)
<p><strong>Kopien:</strong> ${request.copies || 1}</p> );
<p><strong>Status:</strong> ${getStatusText(request.status)}</p> }
</div>
</div>
</div>
${request.reason ? ` if (statusFilter !== 'all') {
<div class="mt-6"> requests = requests.filter(req => req.status === statusFilter);
<h4 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">Begründung</h4> }
<div class="bg-slate-50 dark:bg-slate-700 rounded-lg p-4">
<p class="text-gray-700 dark:text-gray-300">${escapeHtml(request.reason)}</p>
</div>
</div>
` : ''}
<div class="mt-8 flex justify-end space-x-3"> requests.sort((a, b) => {
if (sortOrder === 'oldest') {
return new Date(a.created_at) - new Date(b.created_at);
}
return new Date(b.created_at) - new Date(a.created_at);
});
filteredRequests = requests;
renderRequestsTable();
}
function renderRequestsTable() {
const tableBody = document.getElementById('requests-table-body');
if (!tableBody) return;
if (filteredRequests.length === 0) {
tableBody.innerHTML = '<tr><td colspan="7" class="text-center py-8 text-gray-500">Keine Gastaufträge gefunden</td></tr>';
return;
}
const requestsHtml = filteredRequests.map(request => createRequestRow(request)).join('');
tableBody.innerHTML = requestsHtml;
}
function createRequestRow(request) {
const statusColors = {
'pending': 'bg-yellow-100 text-yellow-800',
'approved': 'bg-green-100 text-green-800',
'rejected': 'bg-red-100 text-red-800'
};
return `
<tr class="hover:bg-gray-50" data-request-id="${request.id}">
<td class="px-6 py-4">
<input type="checkbox" class="request-checkbox" value="${request.id}">
</td>
<td class="px-6 py-4">
<div class="font-medium">${escapeHtml(request.name || 'Unbekannt')}</div>
<div class="text-gray-500">${escapeHtml(request.email || '')}</div>
</td>
<td class="px-6 py-4">
<div class="font-medium">${escapeHtml(request.file_name || 'Keine Datei')}</div>
<div class="text-gray-500">${request.duration_minutes || 0} Min.</div>
</td>
<td class="px-6 py-4">
<span class="px-2 py-1 text-xs rounded-full ${statusColors[request.status] || 'bg-gray-100 text-gray-800'}">
${getStatusText(request.status)}
</span>
</td>
<td class="px-6 py-4 text-gray-500">
${formatDateTime(request.created_at)}
</td>
<td class="px-6 py-4">Normal</td>
<td class="px-6 py-4">
<div class="flex space-x-2">
${request.status === 'pending' ? ` ${request.status === 'pending' ? `
<button onclick="approveRequest(${request.id}); closeDetailModal();" <button onclick="approveRequest(${request.id})" class="text-green-600 hover:text-green-900" title="Genehmigen">
class="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors">
Genehmigen
</button> </button>
<button onclick="rejectRequest(${request.id}); closeDetailModal();" <button onclick="rejectRequest(${request.id})" class="text-red-600 hover:text-red-900" title="Ablehnen">
class="px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors">
Ablehnen
</button> </button>
` : ''} ` : ''}
<button onclick="closeDetailModal()" <button onclick="deleteRequest(${request.id})" class="text-gray-600 hover:text-gray-900" title="Löschen">
class="px-4 py-2 bg-gray-300 dark:bg-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-400 dark:hover:bg-gray-500 transition-colors"> 🗑
Schließen
</button> </button>
</div> </div>
</div> </td>
</tr>
`; `;
modal.classList.remove('hidden');
} }
function closeDetailModal() { // Event Handlers
const modal = document.getElementById('detail-modal'); function handleSearch() { applyFiltersAndSort(); }
modal.classList.add('hidden'); function handleFilterChange() { applyFiltersAndSort(); }
function handleSortChange() { applyFiltersAndSort(); }
function handleSelectAll(event) {
document.querySelectorAll('.request-checkbox').forEach(cb => cb.checked = event.target.checked);
}
function handleExport() { showNotification('Export-Funktion wird implementiert', 'info'); }
// UI Functions
function showLoading(show) {
const overlay = document.getElementById('loading-overlay');
if (overlay) overlay.classList.toggle('hidden', !show);
}
function showEmptyState() {
document.getElementById('empty-state')?.classList.remove('hidden');
}
function showNotification(message, type = 'info') {
const colors = {
'success': 'bg-green-500',
'error': 'bg-red-500',
'warning': 'bg-yellow-500',
'info': 'bg-blue-500'
};
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 ${colors[type]} text-white px-6 py-3 rounded-lg shadow-lg z-50`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 5000);
}
function startAutoRefresh() {
setInterval(loadGuestRequests, 30000); // Refresh every 30 seconds
} }
/**
* Bulk Actions
*/
function showBulkActionsModal() { function showBulkActionsModal() {
const selectedIds = getSelectedRequestIds(); const selectedIds = Array.from(document.querySelectorAll('.request-checkbox:checked')).map(cb => cb.value);
if (selectedIds.length === 0) { if (selectedIds.length === 0) {
showNotification('⚠️ Bitte wählen Sie mindestens einen Gastauftrag aus', 'warning'); showNotification('⚠️ Bitte wählen Sie mindestens einen Gastauftrag aus', 'warning');
return; return;
} }
document.getElementById('bulk-modal')?.classList.remove('hidden');
const modal = document.getElementById('bulk-modal');
modal.classList.remove('hidden');
} }
function closeBulkModal() { function closeBulkModal() {
const modal = document.getElementById('bulk-modal'); document.getElementById('bulk-modal')?.classList.add('hidden');
modal.classList.add('hidden');
}
async function performBulkAction(action) {
const selectedIds = getSelectedRequestIds();
if (selectedIds.length === 0) return;
const confirmMessages = {
'approve': `Möchten Sie ${selectedIds.length} Gastaufträge genehmigen?`,
'reject': `Möchten Sie ${selectedIds.length} Gastaufträge ablehnen?`,
'delete': `Möchten Sie ${selectedIds.length} Gastaufträge löschen?`
};
if (!confirm(confirmMessages[action])) return;
try {
showLoading(true);
closeBulkModal();
const promises = selectedIds.map(async (id) => {
const url = `${API_BASE_URL}/api/guest-requests/${id}/${action}`;
const method = action === 'delete' ? 'DELETE' : 'POST';
return fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
}
});
});
const results = await Promise.allSettled(promises);
const successCount = results.filter(r => r.status === 'fulfilled' && r.value.ok).length;
showNotification(`${successCount} von ${selectedIds.length} Aktionen erfolgreich`, 'success');
loadGuestRequests();
// Alle Checkboxen zurücksetzen
document.getElementById('select-all').checked = false;
document.querySelectorAll('.request-checkbox').forEach(cb => cb.checked = false);
} catch (error) {
console.error('Fehler bei Bulk-Aktion:', error);
showNotification('❌ Fehler bei der Bulk-Aktion: ' + error.message, 'error');
} finally {
showLoading(false);
}
}
function getSelectedRequestIds() {
const checkboxes = document.querySelectorAll('.request-checkbox:checked');
return Array.from(checkboxes).map(cb => parseInt(cb.value));
}
/**
* Event Handlers
*/
function handleSearch() {
applyFiltersAndSort();
}
function handleFilterChange() {
applyFiltersAndSort();
}
function handleSortChange() {
applyFiltersAndSort();
}
function handleSelectAll(event) {
const checkboxes = document.querySelectorAll('.request-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = event.target.checked;
});
}
function handleExport() {
const selectedIds = getSelectedRequestIds();
const exportData = selectedIds.length > 0 ?
filteredRequests.filter(req => selectedIds.includes(req.id)) :
filteredRequests;
if (exportData.length === 0) {
showNotification('⚠️ Keine Daten zum Exportieren verfügbar', 'warning');
return;
}
exportToCSV(exportData);
}
/**
* Export-Funktionen
*/
function exportToCSV(data) {
const headers = ['ID', 'Name', 'E-Mail', 'Datei', 'Status', 'Erstellt', 'Dauer (Min)', 'Kopien', 'Begründung'];
const rows = data.map(req => [
req.id,
req.name || '',
req.email || '',
req.file_name || '',
getStatusText(req.status),
formatDateTime(req.created_at),
req.duration_minutes || '',
req.copies || '',
req.reason || ''
]);
const csvContent = [headers, ...rows]
.map(row => row.map(field => `"${String(field).replace(/"/g, '""')}"`).join(','))
.join('\n');
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
if (link.download !== undefined) {
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', `gastauftraege_${new Date().toISOString().split('T')[0]}.csv`);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
showNotification('📄 CSV-Export erfolgreich erstellt', 'success');
}
/**
* Auto-Refresh
*/
function startAutoRefresh() {
// Refresh alle 30 Sekunden
refreshInterval = setInterval(() => {
loadGuestRequests();
}, 30000);
}
function stopAutoRefresh() {
if (refreshInterval) {
clearInterval(refreshInterval);
refreshInterval = null;
}
}
/**
* Utility-Funktionen
*/
function addRowEventListeners() {
// Falls notwendig, können hier zusätzliche Event Listener hinzugefügt werden
}
function showLoading(show) {
const loadingElement = document.getElementById('table-loading');
const tableBody = document.getElementById('requests-table-body');
if (loadingElement) {
loadingElement.classList.toggle('hidden', !show);
}
if (show && tableBody) {
tableBody.innerHTML = '';
}
}
function showEmptyState() {
const emptyState = document.getElementById('empty-state');
if (emptyState) {
emptyState.classList.remove('hidden');
}
}
function hideEmptyState() {
const emptyState = document.getElementById('empty-state');
if (emptyState) {
emptyState.classList.add('hidden');
}
}
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 px-6 py-4 rounded-xl shadow-2xl z-50 transform transition-all duration-500 translate-x-full ${
type === 'success' ? 'bg-green-500 text-white' :
type === 'error' ? 'bg-red-500 text-white' :
type === 'warning' ? 'bg-yellow-500 text-black' :
'bg-blue-500 text-white'
}`;
notification.innerHTML = `
<div class="flex items-center space-x-3">
<span class="text-lg">
${type === 'success' ? '✅' :
type === 'error' ? '❌' :
type === 'warning' ? '⚠️' : ''}
</span>
<span class="font-medium">${message}</span>
</div>
`;
document.body.appendChild(notification);
// Animation einblenden
setTimeout(() => {
notification.classList.remove('translate-x-full');
}, 100);
// Nach 5 Sekunden entfernen
setTimeout(() => {
notification.classList.add('translate-x-full');
setTimeout(() => notification.remove(), 5000);
}, 5000);
}
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
} }
// Utility Functions
function escapeHtml(text) { function escapeHtml(text) {
const map = { const div = document.createElement('div');
'&': '&amp;', div.textContent = text;
'<': '&lt;', return div.innerHTML;
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text ? String(text).replace(/[&<>"']/g, m => map[m]) : '';
} }
function formatDateTime(dateString) { function formatDateTime(dateString) {
if (!dateString) return 'Unbekannt'; return new Date(dateString).toLocaleString('de-DE');
const date = new Date(dateString);
return date.toLocaleString('de-DE', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
} }
function getTimeAgo(dateString) { function getStatusText(status) {
if (!dateString) return 'Unbekannt'; const texts = {
'pending': 'Wartend',
const now = new Date(); 'approved': 'Genehmigt',
const date = new Date(dateString); 'rejected': 'Abgelehnt'
const diffMs = now - date; };
const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); return texts[status] || status;
const diffDays = Math.floor(diffHours / 24);
if (diffDays > 0) {
return `vor ${diffDays} Tag${diffDays === 1 ? '' : 'en'}`;
} else if (diffHours > 0) {
return `vor ${diffHours} Stunde${diffHours === 1 ? '' : 'n'}`;
} else {
const diffMinutes = Math.floor(diffMs / (1000 * 60));
return `vor ${Math.max(1, diffMinutes)} Minute${diffMinutes === 1 ? '' : 'n'}`;
} }
}
// Globale Funktionen für onclick-Handler
window.showRequestDetail = showRequestDetail;
window.approveRequest = approveRequest;
window.rejectRequest = rejectRequest;
window.deleteRequest = deleteRequest;
window.closeDetailModal = closeDetailModal;
window.closeBulkModal = closeBulkModal;
window.performBulkAction = performBulkAction;
console.log('📋 Admin Guest Requests JavaScript vollständig geladen');