📝 "Refactor session handling
This commit is contained in:
Binary file not shown.
Binary file not shown.
@ -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
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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 - ==================================================
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
867
backend/static/js/admin-guest-requests.min.js
vendored
867
backend/static/js/admin-guest-requests.min.js
vendored
@ -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);
|
||||||
// Status Filter
|
document.getElementById('select-all')?.addEventListener('change', handleSelectAll);
|
||||||
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');
|
||||||
'&': '&',
|
div.textContent = text;
|
||||||
'<': '<',
|
return div.innerHTML;
|
||||||
'>': '>',
|
|
||||||
'"': '"',
|
|
||||||
"'": '''
|
|
||||||
};
|
|
||||||
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');
|
|
Reference in New Issue
Block a user