🎉 Feat/Fix: Log Rotation & Integration Improvement 🎉
📚 Added htmx-integration.js for enhanced interactivity 🌟 💄 Refactored backend/logs/dark-light-unified.css for better theming consistency 📝 🐛 Optimized logging files (app.log, data_management.log, etc.) for performance & error tracking 🛡️
This commit is contained in:
@ -32824,3 +32824,4 @@ NameError: name 'send_from_directory' is not defined
|
|||||||
2025-06-15 23:57:41 - [app] app - [DEBUG] DEBUG - Response: 200
|
2025-06-15 23:57:41 - [app] app - [DEBUG] DEBUG - Response: 200
|
||||||
2025-06-15 23:57:42 - [app] app - [DEBUG] DEBUG - Request: GET /sw.js
|
2025-06-15 23:57:42 - [app] app - [DEBUG] DEBUG - Request: GET /sw.js
|
||||||
2025-06-15 23:57:42 - [app] app - [DEBUG] DEBUG - Response: 304
|
2025-06-15 23:57:42 - [app] app - [DEBUG] DEBUG - Response: 304
|
||||||
|
2025-06-16 00:06:52 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: ./database/myp.db
|
||||||
|
@ -437,3 +437,5 @@
|
|||||||
2025-06-15 23:55:05 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
|
2025-06-15 23:55:05 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
|
||||||
2025-06-15 23:55:07 - [data_management] data_management - [INFO] INFO - ✅ Data Management Module initialisiert
|
2025-06-15 23:55:07 - [data_management] data_management - [INFO] INFO - ✅ Data Management Module initialisiert
|
||||||
2025-06-15 23:55:07 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
|
2025-06-15 23:55:07 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
|
||||||
|
2025-06-16 00:06:52 - [data_management] data_management - [INFO] INFO - ✅ Data Management Module initialisiert
|
||||||
|
2025-06-16 00:06:52 - [data_management] data_management - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
|
||||||
|
@ -1293,3 +1293,7 @@
|
|||||||
2025-06-15 23:57:41 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Konnte Energiedaten für Drucker 6 nicht abrufen: module 'PyP100.PyP100' has no attribute 'P110'
|
2025-06-15 23:57:41 - [hardware_integration] hardware_integration - [WARNING] WARNING - ⚠️ Konnte Energiedaten für Drucker 6 nicht abrufen: module 'PyP100.PyP100' has no attribute 'P110'
|
||||||
2025-06-15 23:57:41 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ Energiestatistiken erfolgreich gesammelt: 0/7 Geräte online
|
2025-06-15 23:57:41 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ Energiestatistiken erfolgreich gesammelt: 0/7 Geräte online
|
||||||
2025-06-15 23:57:41 - [hardware_integration] hardware_integration - [INFO] INFO - 📊 Gesamtverbrauch: 0.0W aktuell, 0.0Wh heute
|
2025-06-15 23:57:41 - [hardware_integration] hardware_integration - [INFO] INFO - 📊 Gesamtverbrauch: 0.0W aktuell, 0.0Wh heute
|
||||||
|
2025-06-16 00:06:52 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ PyP100 (TP-Link Tapo) verfügbar
|
||||||
|
2025-06-16 00:06:52 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ Printer Monitor initialisiert
|
||||||
|
2025-06-16 00:06:52 - [hardware_integration] hardware_integration - [INFO] INFO - ✅ Hardware Integration Module initialisiert
|
||||||
|
2025-06-16 00:06:52 - [hardware_integration] hardware_integration - [INFO] INFO - 📊 Massive Konsolidierung: 2 Dateien → 1 Datei (50% Reduktion)
|
||||||
|
@ -857,3 +857,5 @@
|
|||||||
2025-06-15 23:55:08 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestartet (Legacy-Kompatibilität)
|
2025-06-15 23:55:08 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestartet (Legacy-Kompatibilität)
|
||||||
2025-06-15 23:57:55 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestoppt (Legacy-Kompatibilität)
|
2025-06-15 23:57:55 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestoppt (Legacy-Kompatibilität)
|
||||||
2025-06-15 23:57:55 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestoppt (Legacy-Kompatibilität)
|
2025-06-15 23:57:55 - [job_queue_system] job_queue_system - [INFO] INFO - Queue Manager gestoppt (Legacy-Kompatibilität)
|
||||||
|
2025-06-16 00:06:52 - [job_queue_system] job_queue_system - [INFO] INFO - ✅ Job & Queue System Module initialisiert
|
||||||
|
2025-06-16 00:06:52 - [job_queue_system] job_queue_system - [INFO] INFO - 📊 MASSIVE Konsolidierung: 4 Dateien → 1 Datei (75% Reduktion)
|
||||||
|
@ -924,3 +924,4 @@
|
|||||||
2025-06-15 23:55:07 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
2025-06-15 23:55:07 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||||
2025-06-15 23:55:08 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
|
2025-06-15 23:55:08 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet
|
||||||
2025-06-15 23:55:08 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet
|
2025-06-15 23:55:08 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet
|
||||||
|
2025-06-16 00:06:52 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True
|
||||||
|
@ -656,3 +656,5 @@
|
|||||||
2025-06-15 23:55:07 - [security_suite] security_suite - [INFO] INFO - ✅ Security Suite Module initialisiert
|
2025-06-15 23:55:07 - [security_suite] security_suite - [INFO] INFO - ✅ Security Suite Module initialisiert
|
||||||
2025-06-15 23:55:07 - [security_suite] security_suite - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
|
2025-06-15 23:55:07 - [security_suite] security_suite - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
|
||||||
2025-06-15 23:55:08 - [security_suite] security_suite - [INFO] INFO - 🔒 Security Suite initialisiert
|
2025-06-15 23:55:08 - [security_suite] security_suite - [INFO] INFO - 🔒 Security Suite initialisiert
|
||||||
|
2025-06-16 00:06:52 - [security_suite] security_suite - [INFO] INFO - ✅ Security Suite Module initialisiert
|
||||||
|
2025-06-16 00:06:52 - [security_suite] security_suite - [INFO] INFO - 📊 Massive Konsolidierung: 3 Dateien → 1 Datei (67% Reduktion)
|
||||||
|
@ -910,3 +910,4 @@
|
|||||||
2025-06-15 23:56:27 - [tapo_controller] tapo_controller - [WARNING] WARNING - ⚠️ Versuch 2/3 fehlgeschlagen beim einschalten von 192.168.0.100: HTTPConnectionPool(host='192.168.1.101', port=3128): Read timed out. (read timeout=2)
|
2025-06-15 23:56:27 - [tapo_controller] tapo_controller - [WARNING] WARNING - ⚠️ Versuch 2/3 fehlgeschlagen beim einschalten von 192.168.0.100: HTTPConnectionPool(host='192.168.1.101', port=3128): Read timed out. (read timeout=2)
|
||||||
2025-06-15 23:56:30 - [tapo_controller] tapo_controller - [WARNING] WARNING - ⚠️ Versuch 3/3 fehlgeschlagen beim einschalten von 192.168.0.100: HTTPConnectionPool(host='192.168.1.101', port=3128): Read timed out. (read timeout=2)
|
2025-06-15 23:56:30 - [tapo_controller] tapo_controller - [WARNING] WARNING - ⚠️ Versuch 3/3 fehlgeschlagen beim einschalten von 192.168.0.100: HTTPConnectionPool(host='192.168.1.101', port=3128): Read timed out. (read timeout=2)
|
||||||
2025-06-15 23:56:30 - [tapo_controller] tapo_controller - [ERROR] ERROR - ❌ Alle 3 Versuche fehlgeschlagen beim einschalten der Tapo-Steckdose 192.168.0.100
|
2025-06-15 23:56:30 - [tapo_controller] tapo_controller - [ERROR] ERROR - ❌ Alle 3 Versuche fehlgeschlagen beim einschalten der Tapo-Steckdose 192.168.0.100
|
||||||
|
2025-06-16 00:06:52 - [tapo_controller] tapo_controller - [INFO] INFO - ✅ tapo controller initialisiert
|
||||||
|
@ -185,3 +185,4 @@
|
|||||||
2025-06-15 23:54:04 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager mit Session-Caching initialisiert
|
2025-06-15 23:54:04 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager mit Session-Caching initialisiert
|
||||||
2025-06-15 23:55:05 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager mit Session-Caching initialisiert
|
2025-06-15 23:55:05 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager mit Session-Caching initialisiert
|
||||||
2025-06-15 23:55:07 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager mit Session-Caching initialisiert
|
2025-06-15 23:55:07 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager mit Session-Caching initialisiert
|
||||||
|
2025-06-16 00:06:52 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager mit Session-Caching initialisiert
|
||||||
|
@ -599,3 +599,5 @@
|
|||||||
2025-06-15 23:55:05 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)
|
2025-06-15 23:55:05 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)
|
||||||
2025-06-15 23:55:07 - [utilities_collection] utilities_collection - [INFO] INFO - ✅ Utilities Collection initialisiert
|
2025-06-15 23:55:07 - [utilities_collection] utilities_collection - [INFO] INFO - ✅ Utilities Collection initialisiert
|
||||||
2025-06-15 23:55:07 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)
|
2025-06-15 23:55:07 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)
|
||||||
|
2025-06-16 00:06:52 - [utilities_collection] utilities_collection - [INFO] INFO - ✅ Utilities Collection initialisiert
|
||||||
|
2025-06-16 00:06:52 - [utilities_collection] utilities_collection - [INFO] INFO - 🚨 ALLERLETZTE MEGA-Konsolidierung: 12+ Dateien → 1 Datei (90%+ Reduktion)
|
||||||
|
@ -77,30 +77,30 @@
|
|||||||
.dark {
|
.dark {
|
||||||
/* === DARK MODE FOUNDATION === */
|
/* === DARK MODE FOUNDATION === */
|
||||||
--bg-primary: #000000;
|
--bg-primary: #000000;
|
||||||
--bg-secondary: #0a0a0a;
|
--bg-secondary: #0f0f0f;
|
||||||
--bg-tertiary: #1a1a1a;
|
--bg-tertiary: #1f1f1f;
|
||||||
--bg-card: #111111;
|
--bg-card: #1a1a1a;
|
||||||
--bg-surface: #0d0d0d;
|
--bg-surface: #151515;
|
||||||
--bg-overlay: rgba(0, 0, 0, 0.95);
|
--bg-overlay: rgba(0, 0, 0, 0.95);
|
||||||
--bg-modal: rgba(17, 17, 17, 0.98);
|
--bg-modal: rgba(26, 26, 26, 0.98);
|
||||||
|
|
||||||
/* === TEXT COLORS DARK === */
|
/* === TEXT COLORS DARK === */
|
||||||
--text-primary: #ffffff;
|
--text-primary: #ffffff;
|
||||||
--text-secondary: #e2e8f0;
|
--text-secondary: #f1f5f9;
|
||||||
--text-tertiary: #cbd5e1;
|
--text-tertiary: #e2e8f0;
|
||||||
--text-muted: #94a3b8;
|
--text-muted: #cbd5e1;
|
||||||
--text-accent: var(--mb-white);
|
--text-accent: #ffffff;
|
||||||
--text-on-primary: var(--mb-black);
|
--text-on-primary: #000000;
|
||||||
--text-success: #10b981;
|
--text-success: #22c55e;
|
||||||
--text-warning: #f59e0b;
|
--text-warning: #fbbf24;
|
||||||
--text-error: #ef4444;
|
--text-error: #f87171;
|
||||||
|
|
||||||
/* === BORDER COLORS DARK === */
|
/* === BORDER COLORS DARK === */
|
||||||
--border-primary: #1f2937;
|
--border-primary: #2d2d2d;
|
||||||
--border-secondary: #374151;
|
--border-secondary: #404040;
|
||||||
--border-focus: var(--mb-white);
|
--border-focus: #ffffff;
|
||||||
--border-error: #ef4444;
|
--border-error: #f87171;
|
||||||
--border-success: #10b981;
|
--border-success: #22c55e;
|
||||||
|
|
||||||
/* === SHADOWS DARK === */
|
/* === SHADOWS DARK === */
|
||||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||||
@ -110,32 +110,63 @@
|
|||||||
--shadow-modal: 0 25px 50px -12px rgba(0, 0, 0, 0.7);
|
--shadow-modal: 0 25px 50px -12px rgba(0, 0, 0, 0.7);
|
||||||
|
|
||||||
/* === GLASSMORPHISM DARK === */
|
/* === GLASSMORPHISM DARK === */
|
||||||
--glass-bg: rgba(17, 17, 17, 0.8);
|
--glass-bg: rgba(26, 26, 26, 0.85);
|
||||||
--glass-border: rgba(255, 255, 255, 0.1);
|
--glass-border: rgba(255, 255, 255, 0.15);
|
||||||
--glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
--glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||||
--glass-blur: blur(16px);
|
--glass-blur: blur(16px);
|
||||||
|
|
||||||
/* === INTERACTIVE STATES DARK === */
|
/* === INTERACTIVE STATES DARK === */
|
||||||
--hover-bg: rgba(255, 255, 255, 0.04);
|
--hover-bg: rgba(255, 255, 255, 0.08);
|
||||||
--active-bg: rgba(255, 255, 255, 0.08);
|
--active-bg: rgba(255, 255, 255, 0.12);
|
||||||
--focus-ring: 0 0 0 3px rgba(255, 255, 255, 0.1);
|
--focus-ring: 0 0 0 3px rgba(255, 255, 255, 0.2);
|
||||||
|
|
||||||
/* === GRADIENTS DARK === */
|
/* === GRADIENTS DARK === */
|
||||||
--gradient-primary: linear-gradient(135deg, #000000 0%, #0a0a0a 100%);
|
--gradient-primary: linear-gradient(135deg, #000000 0%, #0f0f0f 100%);
|
||||||
--gradient-card: linear-gradient(135deg, #111111 0%, #0d0d0d 100%);
|
--gradient-card: linear-gradient(135deg, #1a1a1a 0%, #151515 100%);
|
||||||
--gradient-modal: linear-gradient(135deg, #1a1a1a 0%, #111111 100%);
|
--gradient-modal: linear-gradient(135deg, #1f1f1f 0%, #1a1a1a 100%);
|
||||||
--gradient-button: linear-gradient(135deg, var(--mb-white) 0%, #f0f0f0 100%);
|
--gradient-button: linear-gradient(135deg, #ffffff 0%, #f5f5f5 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== UNIFIED COMPONENT STYLES ===== */
|
/* ===== UNIFIED COMPONENT STYLES ===== */
|
||||||
|
|
||||||
|
/* === GLOBAL ROUNDED BORDERS === */
|
||||||
|
* {
|
||||||
|
border-radius: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, textarea, select, button {
|
||||||
|
border-radius: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card, .modal, .glass-card, .mercedes-modal {
|
||||||
|
border-radius: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge, .btn-sm {
|
||||||
|
border-radius: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar, .profile-image {
|
||||||
|
border-radius: 50% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === ENHANCED SPACING SYSTEM === */
|
||||||
|
.spacing-xs { margin: 0.25rem; padding: 0.25rem; }
|
||||||
|
.spacing-sm { margin: 0.5rem; padding: 0.5rem; }
|
||||||
|
.spacing-md { margin: 1rem; padding: 1rem; }
|
||||||
|
.spacing-lg { margin: 1.5rem; padding: 1.5rem; }
|
||||||
|
.spacing-xl { margin: 2rem; padding: 2rem; }
|
||||||
|
.spacing-2xl { margin: 3rem; padding: 3rem; }
|
||||||
|
|
||||||
/* === BUTTONS === */
|
/* === BUTTONS === */
|
||||||
.btn {
|
.btn {
|
||||||
@apply inline-flex items-center justify-center px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200;
|
@apply inline-flex items-center justify-center px-6 py-3 text-sm font-medium transition-all duration-200;
|
||||||
background: var(--bg-card);
|
background: var(--bg-card);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
border: 1px solid var(--border-primary);
|
border: 1px solid var(--border-primary);
|
||||||
box-shadow: var(--shadow-sm);
|
box-shadow: var(--shadow-sm);
|
||||||
|
border-radius: 12px !important;
|
||||||
|
margin: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:hover {
|
.btn:hover {
|
||||||
@ -173,10 +204,12 @@
|
|||||||
.card {
|
.card {
|
||||||
background: var(--gradient-card);
|
background: var(--gradient-card);
|
||||||
border: 1px solid var(--border-primary);
|
border: 1px solid var(--border-primary);
|
||||||
border-radius: 16px;
|
border-radius: 20px !important;
|
||||||
box-shadow: var(--shadow-lg);
|
box-shadow: var(--shadow-lg);
|
||||||
backdrop-filter: var(--glass-blur);
|
backdrop-filter: var(--glass-blur);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
margin: 0.75rem;
|
||||||
|
padding: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card:hover {
|
.card:hover {
|
||||||
@ -187,9 +220,11 @@
|
|||||||
.glass-card {
|
.glass-card {
|
||||||
background: var(--glass-bg);
|
background: var(--glass-bg);
|
||||||
border: 1px solid var(--glass-border);
|
border: 1px solid var(--glass-border);
|
||||||
border-radius: 16px;
|
border-radius: 20px !important;
|
||||||
box-shadow: var(--glass-shadow);
|
box-shadow: var(--glass-shadow);
|
||||||
backdrop-filter: var(--glass-blur);
|
backdrop-filter: var(--glass-blur);
|
||||||
|
margin: 0.75rem;
|
||||||
|
padding: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === MODALS === */
|
/* === MODALS === */
|
||||||
@ -327,10 +362,13 @@
|
|||||||
|
|
||||||
/* === FORM ELEMENTS === */
|
/* === FORM ELEMENTS === */
|
||||||
.form-input {
|
.form-input {
|
||||||
@apply w-full px-4 py-3 rounded-lg transition-all duration-200;
|
@apply w-full transition-all duration-200;
|
||||||
background: var(--bg-card);
|
background: var(--bg-card);
|
||||||
border: 1px solid var(--border-primary);
|
border: 1px solid var(--border-primary);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
|
border-radius: 12px !important;
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
margin: 0.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input:focus {
|
.form-input:focus {
|
||||||
@ -344,14 +382,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.form-label {
|
.form-label {
|
||||||
@apply block text-sm font-medium mb-2;
|
@apply block text-sm font-medium;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === NAVIGATION === */
|
/* === NAVIGATION === */
|
||||||
.nav-item {
|
.nav-item {
|
||||||
@apply px-4 py-2 rounded-lg transition-all duration-200;
|
@apply transition-all duration-200;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
|
border-radius: 12px !important;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
margin: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:hover {
|
.nav-item:hover {
|
||||||
@ -366,9 +409,12 @@
|
|||||||
|
|
||||||
/* === ALERTS === */
|
/* === ALERTS === */
|
||||||
.alert {
|
.alert {
|
||||||
@apply p-4 rounded-lg border;
|
@apply border;
|
||||||
background: var(--bg-card);
|
background: var(--bg-card);
|
||||||
border-color: var(--border-primary);
|
border-color: var(--border-primary);
|
||||||
|
border-radius: 16px !important;
|
||||||
|
padding: 1.25rem;
|
||||||
|
margin: 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-info {
|
.alert-info {
|
||||||
@ -403,19 +449,24 @@
|
|||||||
/* === TABLES === */
|
/* === TABLES === */
|
||||||
.table {
|
.table {
|
||||||
@apply w-full border-collapse;
|
@apply w-full border-collapse;
|
||||||
|
border-radius: 16px !important;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table th {
|
.table th {
|
||||||
@apply px-6 py-4 text-left text-sm font-medium uppercase tracking-wider;
|
@apply text-left text-sm font-medium uppercase tracking-wider;
|
||||||
background: var(--bg-tertiary);
|
background: var(--bg-tertiary);
|
||||||
color: var(--text-tertiary);
|
color: var(--text-tertiary);
|
||||||
border-bottom: 1px solid var(--border-primary);
|
border-bottom: 1px solid var(--border-primary);
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table td {
|
.table td {
|
||||||
@apply px-6 py-4 text-sm;
|
@apply text-sm;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
border-bottom: 1px solid var(--border-primary);
|
border-bottom: 1px solid var(--border-primary);
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table tbody tr:hover {
|
.table tbody tr:hover {
|
||||||
@ -424,18 +475,24 @@
|
|||||||
|
|
||||||
/* === TOOLTIPS === */
|
/* === TOOLTIPS === */
|
||||||
.tooltip {
|
.tooltip {
|
||||||
@apply absolute z-50 px-3 py-2 text-sm rounded-lg shadow-lg;
|
@apply absolute z-50 text-sm shadow-lg;
|
||||||
background: var(--bg-modal);
|
background: var(--bg-modal);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
border: 1px solid var(--border-primary);
|
border: 1px solid var(--border-primary);
|
||||||
backdrop-filter: var(--glass-blur);
|
backdrop-filter: var(--glass-blur);
|
||||||
|
border-radius: 12px !important;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
margin: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === BADGES & STATUS === */
|
/* === BADGES & STATUS === */
|
||||||
.badge {
|
.badge {
|
||||||
@apply inline-flex items-center px-3 py-1 rounded-full text-xs font-medium;
|
@apply inline-flex items-center text-xs font-medium;
|
||||||
background: var(--bg-tertiary);
|
background: var(--bg-tertiary);
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
|
border-radius: 16px !important;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
margin: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-primary {
|
.badge-primary {
|
||||||
@ -475,14 +532,29 @@
|
|||||||
.modal {
|
.modal {
|
||||||
margin: 1rem;
|
margin: 1rem;
|
||||||
max-width: calc(100% - 2rem);
|
max-width: calc(100% - 2rem);
|
||||||
|
border-radius: 16px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
border-radius: 12px;
|
border-radius: 16px !important;
|
||||||
|
margin: 0.5rem;
|
||||||
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
@apply px-3 py-2 text-xs;
|
@apply text-xs;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
margin: 0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
margin: 0.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
margin: 0.125rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,6 +630,57 @@
|
|||||||
transition-duration: 200ms;
|
transition-duration: 200ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* === ENHANCED DARK MODE CONTRAST === */
|
||||||
|
.dark .btn-primary {
|
||||||
|
background: var(--gradient-button);
|
||||||
|
color: var(--text-on-primary);
|
||||||
|
box-shadow: 0 4px 14px 0 rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .btn-primary:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
box-shadow: 0 6px 20px 0 rgba(255, 255, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .card {
|
||||||
|
border: 1px solid var(--border-primary);
|
||||||
|
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .form-input {
|
||||||
|
border: 1px solid var(--border-primary);
|
||||||
|
background: var(--bg-card);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .form-input:focus {
|
||||||
|
border-color: var(--border-focus);
|
||||||
|
box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .alert-info {
|
||||||
|
background: rgba(0, 115, 206, 0.15);
|
||||||
|
border-color: var(--mb-primary);
|
||||||
|
color: #60a5fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .alert-success {
|
||||||
|
background: rgba(34, 197, 94, 0.15);
|
||||||
|
border-color: var(--text-success);
|
||||||
|
color: #4ade80;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .alert-warning {
|
||||||
|
background: rgba(251, 191, 36, 0.15);
|
||||||
|
border-color: var(--text-warning);
|
||||||
|
color: #fbbf24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .alert-error {
|
||||||
|
background: rgba(248, 113, 113, 0.15);
|
||||||
|
border-color: var(--text-error);
|
||||||
|
color: #f87171;
|
||||||
|
}
|
||||||
|
|
||||||
/* === UTILITY CLASSES === */
|
/* === UTILITY CLASSES === */
|
||||||
.theme-transition {
|
.theme-transition {
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
481
backend/static/js/htmx-integration.js
Normal file
481
backend/static/js/htmx-integration.js
Normal file
@ -0,0 +1,481 @@
|
|||||||
|
/**
|
||||||
|
* HTMX Non-Invasive Integration für MYP Platform
|
||||||
|
* =============================================
|
||||||
|
*
|
||||||
|
* Diese Implementierung stellt sicher, dass HTMX vollständig non-invasiv
|
||||||
|
* geladen wird, ohne das Seitenladen oder bestehende JavaScript-Funktionalität
|
||||||
|
* zu blockieren oder zu beeinträchtigen.
|
||||||
|
*
|
||||||
|
* Features:
|
||||||
|
* - Lazy Loading von HTMX nur wenn benötigt
|
||||||
|
* - Progressive Enhancement Pattern
|
||||||
|
* - Keine Blockierung von kritischen Pfaden
|
||||||
|
* - Graceful Fallback ohne HTMX
|
||||||
|
*
|
||||||
|
* Author: Till Tomczak - MYP Team
|
||||||
|
* Zweck: Non-invasive HTMX Integration für dynamische UI-Updates
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
const HTMX_CONFIG = {
|
||||||
|
cdnUrl: 'https://unpkg.com/htmx.org@1.9.12/dist/htmx.min.js',
|
||||||
|
localFallback: '/static/js/lib/htmx.min.js',
|
||||||
|
loadTimeout: 5000,
|
||||||
|
enableLogging: true,
|
||||||
|
retryAttempts: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
// State Management
|
||||||
|
let htmxLoaded = false;
|
||||||
|
let htmxLoading = false;
|
||||||
|
let loadPromise = null;
|
||||||
|
let pendingRequests = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger mit Namespace
|
||||||
|
*/
|
||||||
|
function log(level, message, data = null) {
|
||||||
|
if (!HTMX_CONFIG.enableLogging) return;
|
||||||
|
|
||||||
|
const prefix = '[HTMX-Integration]';
|
||||||
|
const styles = {
|
||||||
|
info: 'color: #0073ce; font-weight: bold;',
|
||||||
|
warn: 'color: #f59e0b; font-weight: bold;',
|
||||||
|
error: 'color: #ef4444; font-weight: bold;',
|
||||||
|
success: 'color: #10b981; font-weight: bold;'
|
||||||
|
};
|
||||||
|
|
||||||
|
console[level === 'success' ? 'log' : level](
|
||||||
|
`%c${prefix} ${message}`,
|
||||||
|
styles[level] || styles.info,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft ob HTMX bereits verfügbar ist
|
||||||
|
*/
|
||||||
|
function isHtmxAvailable() {
|
||||||
|
return typeof window.htmx !== 'undefined' && window.htmx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lädt HTMX dynamisch und non-invasiv
|
||||||
|
*/
|
||||||
|
async function loadHtmx() {
|
||||||
|
if (htmxLoaded || isHtmxAvailable()) {
|
||||||
|
log('info', 'HTMX bereits verfügbar');
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (htmxLoading) {
|
||||||
|
log('info', 'HTMX wird bereits geladen, verwende existierendes Promise');
|
||||||
|
return loadPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
htmxLoading = true;
|
||||||
|
log('info', 'Beginne HTMX-Laden...');
|
||||||
|
|
||||||
|
loadPromise = new Promise((resolve, reject) => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.type = 'text/javascript';
|
||||||
|
script.async = true;
|
||||||
|
script.defer = true;
|
||||||
|
|
||||||
|
// Timeout Handler
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
script.remove();
|
||||||
|
htmxLoading = false;
|
||||||
|
reject(new Error('HTMX laden timeout'));
|
||||||
|
}, HTMX_CONFIG.loadTimeout);
|
||||||
|
|
||||||
|
// Success Handler
|
||||||
|
script.onload = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
htmxLoaded = true;
|
||||||
|
htmxLoading = false;
|
||||||
|
log('success', 'HTMX erfolgreich geladen');
|
||||||
|
|
||||||
|
// HTMX konfigurieren
|
||||||
|
configureHtmx();
|
||||||
|
|
||||||
|
// Pending Requests verarbeiten
|
||||||
|
processPendingRequests();
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Error Handler mit Fallback
|
||||||
|
script.onerror = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
log('warn', 'CDN-Laden fehlgeschlagen, versuche lokalen Fallback...');
|
||||||
|
|
||||||
|
// Lokaler Fallback
|
||||||
|
const fallbackScript = document.createElement('script');
|
||||||
|
fallbackScript.src = HTMX_CONFIG.localFallback;
|
||||||
|
fallbackScript.async = true;
|
||||||
|
fallbackScript.defer = true;
|
||||||
|
|
||||||
|
fallbackScript.onload = () => {
|
||||||
|
htmxLoaded = true;
|
||||||
|
htmxLoading = false;
|
||||||
|
log('success', 'HTMX über lokalen Fallback geladen');
|
||||||
|
configureHtmx();
|
||||||
|
processPendingRequests();
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
fallbackScript.onerror = () => {
|
||||||
|
htmxLoading = false;
|
||||||
|
log('error', 'HTMX konnte nicht geladen werden (CDN + Fallback fehlgeschlagen)');
|
||||||
|
reject(new Error('HTMX nicht verfügbar'));
|
||||||
|
};
|
||||||
|
|
||||||
|
document.head.appendChild(fallbackScript);
|
||||||
|
script.remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
// CDN laden
|
||||||
|
script.src = HTMX_CONFIG.cdnUrl;
|
||||||
|
document.head.appendChild(script);
|
||||||
|
});
|
||||||
|
|
||||||
|
return loadPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Konfiguriert HTMX nach dem Laden
|
||||||
|
*/
|
||||||
|
function configureHtmx() {
|
||||||
|
if (!isHtmxAvailable()) {
|
||||||
|
log('warn', 'HTMX nicht verfügbar für Konfiguration');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// HTMX Konfiguration
|
||||||
|
htmx.config.selfRequestsOnly = true;
|
||||||
|
htmx.config.allowEval = false;
|
||||||
|
htmx.config.allowScriptTags = false;
|
||||||
|
htmx.config.historyCacheSize = 10;
|
||||||
|
htmx.config.timeout = 30000;
|
||||||
|
htmx.config.withCredentials = true;
|
||||||
|
htmx.config.defaultSwapStyle = 'outerHTML';
|
||||||
|
htmx.config.defaultSwapDelay = 0;
|
||||||
|
htmx.config.defaultSettleDelay = 20;
|
||||||
|
|
||||||
|
// CSRF Token Integration
|
||||||
|
const csrfToken = document.querySelector('meta[name="csrf-token"]');
|
||||||
|
if (csrfToken) {
|
||||||
|
htmx.config.requestClass = 'htmx-request';
|
||||||
|
|
||||||
|
// Event Listener für CSRF Token
|
||||||
|
document.body.addEventListener('htmx:configRequest', function(evt) {
|
||||||
|
const token = document.querySelector('meta[name="csrf-token"]');
|
||||||
|
if (token) {
|
||||||
|
evt.detail.headers['X-CSRFToken'] = token.getAttribute('content');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error Handling
|
||||||
|
document.body.addEventListener('htmx:responseError', function(evt) {
|
||||||
|
log('error', 'HTMX Response Error', {
|
||||||
|
status: evt.detail.xhr.status,
|
||||||
|
response: evt.detail.xhr.responseText
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fallback zu Standard-Navigation bei schweren Fehlern
|
||||||
|
if (evt.detail.xhr.status >= 500) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Success Handling
|
||||||
|
document.body.addEventListener('htmx:afterRequest', function(evt) {
|
||||||
|
log('info', 'HTMX Request completed', {
|
||||||
|
method: evt.detail.requestConfig.verb,
|
||||||
|
url: evt.detail.requestConfig.path,
|
||||||
|
status: evt.detail.xhr.status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Loading States
|
||||||
|
document.body.addEventListener('htmx:beforeRequest', function(evt) {
|
||||||
|
addLoadingState(evt.detail.elt);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener('htmx:afterRequest', function(evt) {
|
||||||
|
removeLoadingState(evt.detail.elt);
|
||||||
|
});
|
||||||
|
|
||||||
|
log('success', 'HTMX konfiguration abgeschlossen');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
log('error', 'Fehler bei HTMX-Konfiguration:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt Loading State zu Elementen hinzu
|
||||||
|
*/
|
||||||
|
function addLoadingState(element) {
|
||||||
|
if (!element) return;
|
||||||
|
|
||||||
|
element.classList.add('htmx-loading');
|
||||||
|
element.style.opacity = '0.7';
|
||||||
|
element.style.pointerEvents = 'none';
|
||||||
|
|
||||||
|
// Spinner hinzufügen wenn noch nicht vorhanden
|
||||||
|
if (!element.querySelector('.htmx-spinner')) {
|
||||||
|
const spinner = document.createElement('div');
|
||||||
|
spinner.className = 'htmx-spinner inline-block ml-2';
|
||||||
|
spinner.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
|
||||||
|
element.appendChild(spinner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entfernt Loading State von Elementen
|
||||||
|
*/
|
||||||
|
function removeLoadingState(element) {
|
||||||
|
if (!element) return;
|
||||||
|
|
||||||
|
element.classList.remove('htmx-loading');
|
||||||
|
element.style.opacity = '';
|
||||||
|
element.style.pointerEvents = '';
|
||||||
|
|
||||||
|
// Spinner entfernen
|
||||||
|
const spinner = element.querySelector('.htmx-spinner');
|
||||||
|
if (spinner) {
|
||||||
|
spinner.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verarbeitet wartende HTMX-Requests
|
||||||
|
*/
|
||||||
|
function processPendingRequests() {
|
||||||
|
if (!isHtmxAvailable() || pendingRequests.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log('info', `Verarbeite ${pendingRequests.length} wartende HTMX-Requests`);
|
||||||
|
|
||||||
|
pendingRequests.forEach(request => {
|
||||||
|
try {
|
||||||
|
request();
|
||||||
|
} catch (error) {
|
||||||
|
log('error', 'Fehler beim Verarbeiten wartender Request:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pendingRequests = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt HTMX-Attribute zu einem Element hinzu
|
||||||
|
*/
|
||||||
|
function enhanceElement(element, config) {
|
||||||
|
if (!element || typeof config !== 'object') {
|
||||||
|
log('warn', 'Ungültige Parameter für enhanceElement');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = () => {
|
||||||
|
Object.keys(config).forEach(attr => {
|
||||||
|
const htmxAttr = `hx-${attr}`;
|
||||||
|
element.setAttribute(htmxAttr, config[attr]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// HTMX Element verarbeiten lassen
|
||||||
|
if (isHtmxAvailable()) {
|
||||||
|
htmx.process(element);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isHtmxAvailable()) {
|
||||||
|
request();
|
||||||
|
} else {
|
||||||
|
pendingRequests.push(request);
|
||||||
|
loadHtmx().catch(error => {
|
||||||
|
log('error', 'HTMX-Laden für Element-Enhancement fehlgeschlagen:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt ein HTMX-Enhanced Element
|
||||||
|
*/
|
||||||
|
function createEnhancedElement(tagName, config, content = '') {
|
||||||
|
const element = document.createElement(tagName);
|
||||||
|
element.innerHTML = content;
|
||||||
|
|
||||||
|
enhanceElement(element, config);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazy Loading Trigger für HTMX
|
||||||
|
*/
|
||||||
|
function initializeLazyLoading() {
|
||||||
|
// Intersection Observer für lazy loading
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
const element = entry.target;
|
||||||
|
if (element.hasAttribute('data-htmx-lazy')) {
|
||||||
|
loadHtmx().then(() => {
|
||||||
|
log('info', 'HTMX lazy-loaded für sichtbares Element');
|
||||||
|
}).catch(error => {
|
||||||
|
log('error', 'HTMX lazy-loading fehlgeschlagen:', error);
|
||||||
|
});
|
||||||
|
observer.unobserve(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { threshold: 0.1 });
|
||||||
|
|
||||||
|
// Alle lazy-loading Elemente beobachten
|
||||||
|
document.querySelectorAll('[data-htmx-lazy]').forEach(el => {
|
||||||
|
observer.observe(el);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event-basiertes HTMX-Laden
|
||||||
|
*/
|
||||||
|
function initializeEventTriggers() {
|
||||||
|
document.addEventListener('click', (event) => {
|
||||||
|
const target = event.target.closest('[data-htmx-trigger]');
|
||||||
|
if (target) {
|
||||||
|
event.preventDefault();
|
||||||
|
loadHtmx().then(() => {
|
||||||
|
// Nach dem Laden HTMX-Attribute anwenden
|
||||||
|
const config = JSON.parse(target.getAttribute('data-htmx-config') || '{}');
|
||||||
|
enhanceElement(target, config);
|
||||||
|
|
||||||
|
// Original Click Event simulieren
|
||||||
|
target.click();
|
||||||
|
}).catch(error => {
|
||||||
|
log('error', 'HTMX-Laden über Event-Trigger fehlgeschlagen:', error);
|
||||||
|
// Fallback zu Standard-Verhalten
|
||||||
|
window.location.href = target.href || target.getAttribute('data-fallback-url');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-Detection für HTMX-Attribute
|
||||||
|
*/
|
||||||
|
function autoDetectHtmxElements() {
|
||||||
|
const htmxAttributes = [
|
||||||
|
'hx-get', 'hx-post', 'hx-put', 'hx-patch', 'hx-delete',
|
||||||
|
'hx-trigger', 'hx-target', 'hx-swap', 'hx-boost'
|
||||||
|
];
|
||||||
|
|
||||||
|
const hasHtmxElements = htmxAttributes.some(attr =>
|
||||||
|
document.querySelector(`[${attr}]`)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasHtmxElements) {
|
||||||
|
log('info', 'HTMX-Elemente detectiert, lade HTMX...');
|
||||||
|
loadHtmx().catch(error => {
|
||||||
|
log('error', 'Auto-Detection HTMX-Laden fehlgeschlagen:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public API
|
||||||
|
*/
|
||||||
|
window.MYP_HTMX = {
|
||||||
|
load: loadHtmx,
|
||||||
|
enhance: enhanceElement,
|
||||||
|
create: createEnhancedElement,
|
||||||
|
isLoaded: () => htmxLoaded,
|
||||||
|
isAvailable: isHtmxAvailable,
|
||||||
|
config: HTMX_CONFIG
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization
|
||||||
|
*/
|
||||||
|
function initialize() {
|
||||||
|
log('info', 'Initialisiere HTMX Non-Invasive Integration...');
|
||||||
|
|
||||||
|
// Event Listeners registrieren
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
initializeLazyLoading();
|
||||||
|
initializeEventTriggers();
|
||||||
|
autoDetectHtmxElements();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
initializeLazyLoading();
|
||||||
|
initializeEventTriggers();
|
||||||
|
autoDetectHtmxElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
log('success', 'HTMX Integration bereit');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize immediately
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSS für HTMX Loading States
|
||||||
|
*/
|
||||||
|
const htmxStyles = `
|
||||||
|
.htmx-loading {
|
||||||
|
position: relative;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-spinner {
|
||||||
|
display: inline-block;
|
||||||
|
animation: htmx-spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes htmx-spin {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-indicator {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 200ms ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-request .htmx-indicator {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-request.htmx-indicator {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-swapping {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 200ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-settling {
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 200ms ease-in;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// CSS dynamisch injizieren
|
||||||
|
if (!document.getElementById('htmx-integration-styles')) {
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.id = 'htmx-integration-styles';
|
||||||
|
style.textContent = htmxStyles;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
}
|
@ -903,6 +903,9 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- HTMX Non-Invasive Integration (lädt nur bei Bedarf) -->
|
||||||
|
<script src="{{ url_for('static', filename='js/htmx-integration.js') }}"></script>
|
||||||
|
|
||||||
<!-- Jobs Safety Fix laden -->
|
<!-- Jobs Safety Fix laden -->
|
||||||
<script src="{{ url_for('static', filename='js/jobs-safety-fix.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/jobs-safety-fix.js') }}"></script>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user