diff --git a/backend/logs/app/app.log b/backend/logs/app/app.log
index adf714fe7..15247f122 100644
--- a/backend/logs/app/app.log
+++ b/backend/logs/app/app.log
@@ -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:42 - [app] app - [DEBUG] DEBUG - Request: GET /sw.js
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
diff --git a/backend/logs/data_management/data_management.log b/backend/logs/data_management/data_management.log
index dd264892c..bb794f9ec 100644
--- a/backend/logs/data_management/data_management.log
+++ b/backend/logs/data_management/data_management.log
@@ -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: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-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)
diff --git a/backend/logs/hardware_integration/hardware_integration.log b/backend/logs/hardware_integration/hardware_integration.log
index 2fe7854eb..a22b5ed84 100644
--- a/backend/logs/hardware_integration/hardware_integration.log
+++ b/backend/logs/hardware_integration/hardware_integration.log
@@ -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 - [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-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)
diff --git a/backend/logs/job_queue_system/job_queue_system.log b/backend/logs/job_queue_system/job_queue_system.log
index 53c429e9b..78eb8f413 100644
--- a/backend/logs/job_queue_system/job_queue_system.log
+++ b/backend/logs/job_queue_system/job_queue_system.log
@@ -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: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)
diff --git a/backend/logs/scheduler/scheduler.log b/backend/logs/scheduler/scheduler.log
index b9a466e68..01f226a7c 100644
--- a/backend/logs/scheduler/scheduler.log
+++ b/backend/logs/scheduler/scheduler.log
@@ -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:08 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread 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
diff --git a/backend/logs/security_suite/security_suite.log b/backend/logs/security_suite/security_suite.log
index aa875bc3c..bce0a3b70 100644
--- a/backend/logs/security_suite/security_suite.log
+++ b/backend/logs/security_suite/security_suite.log
@@ -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 - 📊 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-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)
diff --git a/backend/logs/tapo_controller/tapo_controller.log b/backend/logs/tapo_controller/tapo_controller.log
index 608e1f3d3..a603431ed 100644
--- a/backend/logs/tapo_controller/tapo_controller.log
+++ b/backend/logs/tapo_controller/tapo_controller.log
@@ -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: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-16 00:06:52 - [tapo_controller] tapo_controller - [INFO] INFO - âś… tapo controller initialisiert
diff --git a/backend/logs/tapo_status_manager/tapo_status_manager.log b/backend/logs/tapo_status_manager/tapo_status_manager.log
index 17d90927c..d32e446a6 100644
--- a/backend/logs/tapo_status_manager/tapo_status_manager.log
+++ b/backend/logs/tapo_status_manager/tapo_status_manager.log
@@ -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: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-16 00:06:52 - [tapo_status_manager] tapo_status_manager - [INFO] INFO - TapoStatusManager mit Session-Caching initialisiert
diff --git a/backend/logs/utilities_collection/utilities_collection.log b/backend/logs/utilities_collection/utilities_collection.log
index e0a785e06..cd3db4f6b 100644
--- a/backend/logs/utilities_collection/utilities_collection.log
+++ b/backend/logs/utilities_collection/utilities_collection.log
@@ -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: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-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)
diff --git a/backend/static/css/dark-light-unified.css b/backend/static/css/dark-light-unified.css
index 209a0b8ad..5bc23b5d2 100644
--- a/backend/static/css/dark-light-unified.css
+++ b/backend/static/css/dark-light-unified.css
@@ -77,30 +77,30 @@
.dark {
/* === DARK MODE FOUNDATION === */
--bg-primary: #000000;
- --bg-secondary: #0a0a0a;
- --bg-tertiary: #1a1a1a;
- --bg-card: #111111;
- --bg-surface: #0d0d0d;
+ --bg-secondary: #0f0f0f;
+ --bg-tertiary: #1f1f1f;
+ --bg-card: #1a1a1a;
+ --bg-surface: #151515;
--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-primary: #ffffff;
- --text-secondary: #e2e8f0;
- --text-tertiary: #cbd5e1;
- --text-muted: #94a3b8;
- --text-accent: var(--mb-white);
- --text-on-primary: var(--mb-black);
- --text-success: #10b981;
- --text-warning: #f59e0b;
- --text-error: #ef4444;
+ --text-secondary: #f1f5f9;
+ --text-tertiary: #e2e8f0;
+ --text-muted: #cbd5e1;
+ --text-accent: #ffffff;
+ --text-on-primary: #000000;
+ --text-success: #22c55e;
+ --text-warning: #fbbf24;
+ --text-error: #f87171;
/* === BORDER COLORS DARK === */
- --border-primary: #1f2937;
- --border-secondary: #374151;
- --border-focus: var(--mb-white);
- --border-error: #ef4444;
- --border-success: #10b981;
+ --border-primary: #2d2d2d;
+ --border-secondary: #404040;
+ --border-focus: #ffffff;
+ --border-error: #f87171;
+ --border-success: #22c55e;
/* === SHADOWS DARK === */
--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);
/* === GLASSMORPHISM DARK === */
- --glass-bg: rgba(17, 17, 17, 0.8);
- --glass-border: rgba(255, 255, 255, 0.1);
- --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
+ --glass-bg: rgba(26, 26, 26, 0.85);
+ --glass-border: rgba(255, 255, 255, 0.15);
+ --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
--glass-blur: blur(16px);
/* === INTERACTIVE STATES DARK === */
- --hover-bg: rgba(255, 255, 255, 0.04);
- --active-bg: rgba(255, 255, 255, 0.08);
- --focus-ring: 0 0 0 3px rgba(255, 255, 255, 0.1);
+ --hover-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.2);
/* === GRADIENTS DARK === */
- --gradient-primary: linear-gradient(135deg, #000000 0%, #0a0a0a 100%);
- --gradient-card: linear-gradient(135deg, #111111 0%, #0d0d0d 100%);
- --gradient-modal: linear-gradient(135deg, #1a1a1a 0%, #111111 100%);
- --gradient-button: linear-gradient(135deg, var(--mb-white) 0%, #f0f0f0 100%);
+ --gradient-primary: linear-gradient(135deg, #000000 0%, #0f0f0f 100%);
+ --gradient-card: linear-gradient(135deg, #1a1a1a 0%, #151515 100%);
+ --gradient-modal: linear-gradient(135deg, #1f1f1f 0%, #1a1a1a 100%);
+ --gradient-button: linear-gradient(135deg, #ffffff 0%, #f5f5f5 100%);
}
/* ===== 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 === */
.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);
color: var(--text-primary);
border: 1px solid var(--border-primary);
box-shadow: var(--shadow-sm);
+ border-radius: 12px !important;
+ margin: 0.25rem;
}
.btn:hover {
@@ -173,10 +204,12 @@
.card {
background: var(--gradient-card);
border: 1px solid var(--border-primary);
- border-radius: 16px;
+ border-radius: 20px !important;
box-shadow: var(--shadow-lg);
backdrop-filter: var(--glass-blur);
transition: all 0.3s ease;
+ margin: 0.75rem;
+ padding: 1.5rem;
}
.card:hover {
@@ -187,9 +220,11 @@
.glass-card {
background: var(--glass-bg);
border: 1px solid var(--glass-border);
- border-radius: 16px;
+ border-radius: 20px !important;
box-shadow: var(--glass-shadow);
backdrop-filter: var(--glass-blur);
+ margin: 0.75rem;
+ padding: 1.5rem;
}
/* === MODALS === */
@@ -327,10 +362,13 @@
/* === FORM ELEMENTS === */
.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);
border: 1px solid var(--border-primary);
color: var(--text-primary);
+ border-radius: 12px !important;
+ padding: 1rem 1.25rem;
+ margin: 0.5rem 0;
}
.form-input:focus {
@@ -344,14 +382,19 @@
}
.form-label {
- @apply block text-sm font-medium mb-2;
+ @apply block text-sm font-medium;
color: var(--text-secondary);
+ margin-bottom: 0.5rem;
+ margin-top: 1rem;
}
/* === NAVIGATION === */
.nav-item {
- @apply px-4 py-2 rounded-lg transition-all duration-200;
+ @apply transition-all duration-200;
color: var(--text-secondary);
+ border-radius: 12px !important;
+ padding: 0.75rem 1rem;
+ margin: 0.25rem;
}
.nav-item:hover {
@@ -366,9 +409,12 @@
/* === ALERTS === */
.alert {
- @apply p-4 rounded-lg border;
+ @apply border;
background: var(--bg-card);
border-color: var(--border-primary);
+ border-radius: 16px !important;
+ padding: 1.25rem;
+ margin: 1rem 0;
}
.alert-info {
@@ -403,19 +449,24 @@
/* === TABLES === */
.table {
@apply w-full border-collapse;
+ border-radius: 16px !important;
+ overflow: hidden;
+ margin: 1rem 0;
}
.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);
color: var(--text-tertiary);
border-bottom: 1px solid var(--border-primary);
+ padding: 1rem 1.5rem;
}
.table td {
- @apply px-6 py-4 text-sm;
+ @apply text-sm;
color: var(--text-secondary);
border-bottom: 1px solid var(--border-primary);
+ padding: 1rem 1.5rem;
}
.table tbody tr:hover {
@@ -424,18 +475,24 @@
/* === TOOLTIPS === */
.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);
color: var(--text-primary);
border: 1px solid var(--border-primary);
backdrop-filter: var(--glass-blur);
+ border-radius: 12px !important;
+ padding: 0.75rem 1rem;
+ margin: 0.25rem;
}
/* === BADGES & STATUS === */
.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);
color: var(--text-secondary);
+ border-radius: 16px !important;
+ padding: 0.5rem 0.75rem;
+ margin: 0.25rem;
}
.badge-primary {
@@ -475,14 +532,29 @@
.modal {
margin: 1rem;
max-width: calc(100% - 2rem);
+ border-radius: 16px !important;
}
.card {
- border-radius: 12px;
+ border-radius: 16px !important;
+ margin: 0.5rem;
+ padding: 1rem;
}
.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;
}
+/* === 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 === */
.theme-transition {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
diff --git a/backend/static/js/htmx-integration.js b/backend/static/js/htmx-integration.js
new file mode 100644
index 000000000..a4c67e44c
--- /dev/null
+++ b/backend/static/js/htmx-integration.js
@@ -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 = '';
+ 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);
+}
\ No newline at end of file
diff --git a/backend/templates/base.html b/backend/templates/base.html
index 4378fda79..64586a9d9 100644
--- a/backend/templates/base.html
+++ b/backend/templates/base.html
@@ -903,6 +903,9 @@
});
+
+
+