"feat: Integrate Tailwind CSS
This commit is contained in:
parent
626c7c9c57
commit
43fa61dced
BIN
backend/app/database/myp.db-shm
Normal file
BIN
backend/app/database/myp.db-shm
Normal file
Binary file not shown.
BIN
backend/app/database/myp.db-wal
Normal file
BIN
backend/app/database/myp.db-wal
Normal file
Binary file not shown.
2
backend/app/static/css/tailwind.min.css
vendored
2
backend/app/static/css/tailwind.min.css
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 187 B After Width: | Height: | Size: 551 B |
1
backend/app/static/js/csp-violation-handler.js
Normal file
1
backend/app/static/js/csp-violation-handler.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
482
backend/app/static/js/event-handlers.js
Normal file
482
backend/app/static/js/event-handlers.js
Normal file
@ -0,0 +1,482 @@
|
|||||||
|
/**
|
||||||
|
* Mercedes-Benz MYP Platform - Zentrale Event Handler
|
||||||
|
* CSP-konforme Event-Handler ohne Inline-JavaScript
|
||||||
|
*/
|
||||||
|
|
||||||
|
class GlobalEventManager {
|
||||||
|
constructor() {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// Event-Delegation für bessere Performance und CSP-Konformität
|
||||||
|
document.addEventListener('click', this.handleClick.bind(this));
|
||||||
|
document.addEventListener('DOMContentLoaded', this.setupEventListeners.bind(this));
|
||||||
|
|
||||||
|
// Falls DOM bereits geladen
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', this.setupEventListeners.bind(this));
|
||||||
|
} else {
|
||||||
|
this.setupEventListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zentrale Click-Handler mit Event-Delegation
|
||||||
|
*/
|
||||||
|
handleClick(event) {
|
||||||
|
const target = event.target.closest('[data-action]');
|
||||||
|
if (!target) return;
|
||||||
|
|
||||||
|
const action = target.getAttribute('data-action');
|
||||||
|
const params = this.parseActionParams(target);
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
this.executeAction(action, params, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action-Parameter aus data-Attributen parsen
|
||||||
|
*/
|
||||||
|
parseActionParams(element) {
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
// Alle data-action-* Attribute sammeln
|
||||||
|
for (const attr of element.attributes) {
|
||||||
|
if (attr.name.startsWith('data-action-')) {
|
||||||
|
const key = attr.name.replace('data-action-', '');
|
||||||
|
params[key] = attr.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action ausführen basierend auf dem Action-Namen
|
||||||
|
*/
|
||||||
|
executeAction(action, params, element) {
|
||||||
|
console.log(`🎯 Führe Action aus: ${action}`, params);
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
// Dashboard Actions
|
||||||
|
case 'refresh-dashboard':
|
||||||
|
if (typeof refreshDashboard === 'function') refreshDashboard();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Navigation Actions
|
||||||
|
case 'logout':
|
||||||
|
if (typeof handleLogout === 'function') handleLogout();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'go-back':
|
||||||
|
window.history.back();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'reload-page':
|
||||||
|
window.location.reload();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'print-page':
|
||||||
|
window.print();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Job Management Actions
|
||||||
|
case 'refresh-jobs':
|
||||||
|
if (typeof refreshJobs === 'function') refreshJobs();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'toggle-batch-mode':
|
||||||
|
if (typeof toggleBatchMode === 'function') toggleBatchMode();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'start-job':
|
||||||
|
if (typeof jobManager !== 'undefined' && params.id) {
|
||||||
|
jobManager.startJob(params.id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'pause-job':
|
||||||
|
if (typeof jobManager !== 'undefined' && params.id) {
|
||||||
|
jobManager.pauseJob(params.id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'resume-job':
|
||||||
|
if (typeof jobManager !== 'undefined' && params.id) {
|
||||||
|
jobManager.resumeJob(params.id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'delete-job':
|
||||||
|
if (typeof jobManager !== 'undefined' && params.id) {
|
||||||
|
jobManager.deleteJob(params.id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'open-job-details':
|
||||||
|
if (typeof jobManager !== 'undefined' && params.id) {
|
||||||
|
jobManager.openJobDetails(params.id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Printer Management Actions
|
||||||
|
case 'refresh-printers':
|
||||||
|
if (typeof refreshPrinters === 'function') refreshPrinters();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'toggle-maintenance-mode':
|
||||||
|
if (typeof toggleMaintenanceMode === 'function') toggleMaintenanceMode();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'open-add-printer-modal':
|
||||||
|
if (typeof openAddPrinterModal === 'function') openAddPrinterModal();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'toggle-auto-refresh':
|
||||||
|
if (typeof toggleAutoRefresh === 'function') toggleAutoRefresh();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'clear-all-filters':
|
||||||
|
if (typeof clearAllFilters === 'function') clearAllFilters();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'test-printer-connection':
|
||||||
|
if (typeof testPrinterConnection === 'function') testPrinterConnection();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'delete-printer':
|
||||||
|
if (typeof deletePrinter === 'function') deletePrinter();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'edit-printer':
|
||||||
|
if (typeof printerManager !== 'undefined' && params.id) {
|
||||||
|
printerManager.editPrinter(params.id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'connect-printer':
|
||||||
|
if (typeof printerManager !== 'undefined' && params.id) {
|
||||||
|
printerManager.connectPrinter(params.id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Calendar Actions
|
||||||
|
case 'refresh-calendar':
|
||||||
|
if (typeof refreshCalendar === 'function') refreshCalendar();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'toggle-auto-optimization':
|
||||||
|
if (typeof toggleAutoOptimization === 'function') toggleAutoOptimization();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'export-calendar':
|
||||||
|
if (typeof exportCalendar === 'function') exportCalendar();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'open-create-event-modal':
|
||||||
|
if (typeof openCreateEventModal === 'function') openCreateEventModal();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Modal Actions
|
||||||
|
case 'close-modal':
|
||||||
|
this.closeModal(params.target || element.closest('.fixed'));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'close-printer-modal':
|
||||||
|
if (typeof closePrinterModal === 'function') closePrinterModal();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'close-job-modal':
|
||||||
|
if (typeof closeJobModal === 'function') closeJobModal();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'close-event-modal':
|
||||||
|
if (typeof closeEventModal === 'function') closeEventModal();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Form Actions
|
||||||
|
case 'reset-form':
|
||||||
|
if (typeof resetForm === 'function') resetForm();
|
||||||
|
else this.resetNearestForm(element);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'clear-file':
|
||||||
|
if (typeof clearFile === 'function') clearFile();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Guest Request Actions
|
||||||
|
case 'check-status':
|
||||||
|
if (typeof checkStatus === 'function') checkStatus();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'copy-code':
|
||||||
|
if (typeof copyCode === 'function') copyCode();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'refresh-status':
|
||||||
|
if (typeof refreshStatus === 'function') refreshStatus();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'show-status-check':
|
||||||
|
if (typeof showStatusCheck === 'function') showStatusCheck();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Admin Actions
|
||||||
|
case 'perform-bulk-action':
|
||||||
|
if (typeof performBulkAction === 'function' && params.type) {
|
||||||
|
performBulkAction(params.type);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'close-bulk-modal':
|
||||||
|
if (typeof closeBulkModal === 'function') closeBulkModal();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'clear-cache':
|
||||||
|
if (typeof clearCache === 'function') clearCache();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'optimize-database':
|
||||||
|
if (typeof optimizeDatabase === 'function') optimizeDatabase();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'create-backup':
|
||||||
|
if (typeof createBackup === 'function') createBackup();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'download-logs':
|
||||||
|
if (typeof downloadLogs === 'function') downloadLogs();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'run-maintenance':
|
||||||
|
if (typeof runMaintenance === 'function') runMaintenance();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'save-settings':
|
||||||
|
if (typeof saveSettings === 'function') saveSettings();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Profile Actions
|
||||||
|
case 'toggle-edit-mode':
|
||||||
|
if (typeof toggleEditMode === 'function') toggleEditMode();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'trigger-avatar-upload':
|
||||||
|
if (typeof triggerAvatarUpload === 'function') triggerAvatarUpload();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'cancel-edit':
|
||||||
|
if (typeof cancelEdit === 'function') cancelEdit();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'download-user-data':
|
||||||
|
if (typeof downloadUserData === 'function') downloadUserData();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Stats Actions
|
||||||
|
case 'refresh-stats':
|
||||||
|
if (typeof refreshStats === 'function') refreshStats();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'export-stats':
|
||||||
|
if (typeof exportStats === 'function') exportStats();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Generic Actions
|
||||||
|
case 'remove-element':
|
||||||
|
const targetElement = params.target ?
|
||||||
|
document.querySelector(params.target) :
|
||||||
|
element.closest(params.selector || '.removable');
|
||||||
|
if (targetElement) {
|
||||||
|
targetElement.remove();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'toggle-element':
|
||||||
|
const toggleTarget = params.target ?
|
||||||
|
document.querySelector(params.target) :
|
||||||
|
element.nextElementSibling;
|
||||||
|
if (toggleTarget) {
|
||||||
|
toggleTarget.classList.toggle('hidden');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'show-element':
|
||||||
|
const showTarget = document.querySelector(params.target);
|
||||||
|
if (showTarget) {
|
||||||
|
showTarget.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'hide-element':
|
||||||
|
const hideTarget = document.querySelector(params.target);
|
||||||
|
if (hideTarget) {
|
||||||
|
hideTarget.classList.add('hidden');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn(`⚠️ Unbekannte Action: ${action}`);
|
||||||
|
// Versuche globale Funktion zu finden
|
||||||
|
if (typeof window[action] === 'function') {
|
||||||
|
window[action](params);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generische Modal-Schließfunktion
|
||||||
|
*/
|
||||||
|
closeModal(modalElement) {
|
||||||
|
if (modalElement) {
|
||||||
|
modalElement.classList.add('hidden');
|
||||||
|
modalElement.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nächstes Formular zurücksetzen
|
||||||
|
*/
|
||||||
|
resetNearestForm(element) {
|
||||||
|
const form = element.closest('form');
|
||||||
|
if (form) {
|
||||||
|
form.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spezifische Event-Listener einrichten
|
||||||
|
*/
|
||||||
|
setupEventListeners() {
|
||||||
|
// Auto-Refresh für bestimmte Seiten
|
||||||
|
this.setupAutoRefresh();
|
||||||
|
|
||||||
|
// Keyboard Shortcuts
|
||||||
|
this.setupKeyboardShortcuts();
|
||||||
|
|
||||||
|
// Form Validierung
|
||||||
|
this.setupFormValidation();
|
||||||
|
|
||||||
|
console.log('🔧 Globale Event-Handler initialisiert');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-Refresh für Dashboard/Jobs einrichten
|
||||||
|
*/
|
||||||
|
setupAutoRefresh() {
|
||||||
|
const currentPath = window.location.pathname;
|
||||||
|
|
||||||
|
// Auto-refresh für Dashboard alle 30 Sekunden
|
||||||
|
if (currentPath.includes('/dashboard')) {
|
||||||
|
setInterval(() => {
|
||||||
|
if (typeof refreshDashboard === 'function') {
|
||||||
|
refreshDashboard();
|
||||||
|
}
|
||||||
|
}, 30000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-refresh für Jobs alle 15 Sekunden
|
||||||
|
if (currentPath.includes('/jobs')) {
|
||||||
|
setInterval(() => {
|
||||||
|
if (typeof refreshJobs === 'function') {
|
||||||
|
refreshJobs();
|
||||||
|
}
|
||||||
|
}, 15000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keyboard Shortcuts einrichten
|
||||||
|
*/
|
||||||
|
setupKeyboardShortcuts() {
|
||||||
|
document.addEventListener('keydown', (event) => {
|
||||||
|
// ESC zum Schließen von Modals
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
const openModal = document.querySelector('.fixed:not(.hidden)');
|
||||||
|
if (openModal) {
|
||||||
|
this.closeModal(openModal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctrl+R für Refresh (überschreiben für spezifische Funktionen)
|
||||||
|
if (event.ctrlKey && event.key === 'r') {
|
||||||
|
event.preventDefault();
|
||||||
|
const currentPath = window.location.pathname;
|
||||||
|
|
||||||
|
if (currentPath.includes('/dashboard') && typeof refreshDashboard === 'function') {
|
||||||
|
refreshDashboard();
|
||||||
|
} else if (currentPath.includes('/jobs') && typeof refreshJobs === 'function') {
|
||||||
|
refreshJobs();
|
||||||
|
} else if (currentPath.includes('/printers') && typeof refreshPrinters === 'function') {
|
||||||
|
refreshPrinters();
|
||||||
|
} else {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form-Validierung einrichten
|
||||||
|
*/
|
||||||
|
setupFormValidation() {
|
||||||
|
// Alle Formulare finden und Validierung hinzufügen
|
||||||
|
const forms = document.querySelectorAll('form[data-validate]');
|
||||||
|
forms.forEach(form => {
|
||||||
|
form.addEventListener('submit', this.validateForm.bind(this));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formular validieren
|
||||||
|
*/
|
||||||
|
validateForm(event) {
|
||||||
|
const form = event.target;
|
||||||
|
const requiredFields = form.querySelectorAll('[required]');
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
requiredFields.forEach(field => {
|
||||||
|
if (!field.value.trim()) {
|
||||||
|
isValid = false;
|
||||||
|
field.classList.add('border-red-500');
|
||||||
|
|
||||||
|
// Fehlermeldung anzeigen
|
||||||
|
const errorId = `${field.id}-error`;
|
||||||
|
let errorElement = document.getElementById(errorId);
|
||||||
|
|
||||||
|
if (!errorElement) {
|
||||||
|
errorElement = document.createElement('div');
|
||||||
|
errorElement.id = errorId;
|
||||||
|
errorElement.className = 'text-red-500 text-sm mt-1';
|
||||||
|
field.parentNode.appendChild(errorElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
errorElement.textContent = `${field.getAttribute('data-label') || 'Dieses Feld'} ist erforderlich.`;
|
||||||
|
} else {
|
||||||
|
field.classList.remove('border-red-500');
|
||||||
|
const errorElement = document.getElementById(`${field.id}-error`);
|
||||||
|
if (errorElement) {
|
||||||
|
errorElement.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Globale Instanz erstellen
|
||||||
|
const globalEventManager = new GlobalEventManager();
|
||||||
|
|
||||||
|
// Export für Module
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = GlobalEventManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🌍 Globaler Event Manager geladen');
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- Moderne Gastaufträge-Verwaltung -->
|
<!-- Moderne Gastaufträge-Verwaltung -->
|
||||||
<div class="min-h-screen bg-gray-50 dark:bg-slate-950">
|
<div class="min-h-screen">
|
||||||
|
|
||||||
<!-- Hero Header -->
|
<!-- Hero Header -->
|
||||||
<div class="relative overflow-hidden bg-gradient-to-r from-slate-900 via-blue-900 to-indigo-900 dark:from-slate-950 dark:via-blue-950 dark:to-indigo-950 text-white rounded-3xl mx-4 mt-4">
|
<div class="relative overflow-hidden bg-gradient-to-r from-slate-900 via-blue-900 to-indigo-900 dark:from-slate-950 dark:via-blue-950 dark:to-indigo-950 text-white rounded-3xl mx-4 mt-4">
|
||||||
|
@ -580,6 +580,7 @@
|
|||||||
<script src="{{ url_for('static', filename='js/dark-mode-fix.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/dark-mode-fix.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/optimization-features.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/optimization-features.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/global-refresh-functions.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/global-refresh-functions.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/event-handlers.js') }}"></script>
|
||||||
{% if current_user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
<script src="{{ url_for('static', filename='js/notifications.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/notifications.js') }}"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user