"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 %}
|
||||
<!-- Moderne Gastaufträge-Verwaltung -->
|
||||
<div class="min-h-screen bg-gray-50 dark:bg-slate-950">
|
||||
<div class="min-h-screen">
|
||||
|
||||
<!-- 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">
|
||||
|
@ -580,6 +580,7 @@
|
||||
<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/global-refresh-functions.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/event-handlers.js') }}"></script>
|
||||
{% if current_user.is_authenticated %}
|
||||
<script src="{{ url_for('static', filename='js/notifications.js') }}"></script>
|
||||
{% endif %}
|
||||
|
Loading…
x
Reference in New Issue
Block a user