Files
Projektarbeit-MYP/backend/app/templates/printers.html
Till Tomczak 2d33753b94 feat: Major updates to backend structure and security enhancements
- Removed `COMMON_ERRORS.md` file to streamline documentation.
- Added `Flask-Limiter` for rate limiting and `redis` for session management in `requirements.txt`.
- Expanded `ROADMAP.md` to include completed security features and planned enhancements for version 2.2.
- Enhanced `setup_myp.sh` for ultra-secure kiosk installation, including system hardening and security configurations.
- Updated `app.py` to integrate CSRF protection and improved logging setup.
- Refactored user model to include username and active status for better user management.
- Improved job scheduler with uptime tracking and task management features.
- Updated various templates for a more cohesive user interface and experience.
2025-05-25 20:33:38 +02:00

517 lines
27 KiB
HTML

{% extends "base.html" %}
{% block title %}Drucker - MYP Platform{% endblock %}
{% block content %}
<div class="max-w-7xl mx-auto px-3 sm:px-6 lg:px-8 py-4 sm:py-8">
<!-- Header -->
<div class="mb-4 sm:mb-8">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between">
<div class="mb-4 sm:mb-0">
<h1 class="text-2xl sm:text-3xl font-bold text-slate-900 dark:text-white">Drucker</h1>
<p class="mt-1 sm:mt-2 text-sm sm:text-base text-slate-600 dark:text-slate-400">Verwalten Sie Ihre 3D-Drucker</p>
</div>
<div class="flex flex-wrap gap-2 sm:space-x-4">
<button onclick="refreshPrinters()" class="flex-1 sm:flex-none bg-indigo-600 hover:bg-indigo-700 dark:bg-indigo-600 dark:hover:bg-indigo-700 text-white px-3 sm:px-4 py-2 rounded-lg transition-all duration-200 text-sm sm:text-base flex items-center justify-center">
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
Aktualisieren
</button>
{% if current_user.is_admin %}
<button id="addPrinterBtn" class="flex-1 sm:flex-none bg-green-600 hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-700 text-white px-3 sm:px-4 py-2 rounded-lg transition-all duration-200 text-sm sm:text-base flex items-center justify-center">
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
Drucker hinzufügen
</button>
{% endif %}
</div>
</div>
</div>
<!-- Printers Grid -->
<div id="printers-grid" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-6">
<!-- Loading state -->
<div class="col-span-full text-center py-6 sm:py-12">
<div class="animate-spin rounded-full h-8 w-8 sm:h-12 sm:w-12 border-b-2 border-indigo-600 dark:border-indigo-400 mx-auto"></div>
<p class="mt-3 sm:mt-4 text-sm sm:text-base text-slate-600 dark:text-slate-400">Lade Drucker...</p>
</div>
</div>
</div>
<!-- Add Printer Modal -->
{% if current_user.is_admin %}
<div id="addPrinterModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50">
<div class="flex items-center justify-center min-h-screen p-4">
<div class="bg-white dark:bg-slate-800 rounded-xl p-4 sm:p-6 w-full max-w-md shadow-lg">
<div class="flex items-center justify-between mb-4 sm:mb-6">
<h2 class="text-lg sm:text-xl font-bold text-slate-900 dark:text-white">Neuen Drucker hinzufügen</h2>
<button id="closeAddPrinterBtn" class="text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white">
<svg class="h-5 w-5 sm:h-6 sm:w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<form id="addPrinterForm" class="space-y-3 sm:space-y-4">
<div>
<label for="printer-name" class="block text-sm font-medium text-slate-900 dark:text-white mb-1 sm:mb-2">Name</label>
<input type="text" id="printer-name" name="name" required
class="w-full px-3 py-2 border border-slate-300 dark:border-slate-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-slate-700 dark:text-white">
</div>
<div>
<label for="printer-model" class="block text-sm font-medium text-slate-900 dark:text-white mb-1 sm:mb-2">Modell</label>
<input type="text" id="printer-model" name="model" required
class="w-full px-3 py-2 border border-slate-300 dark:border-slate-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-slate-700 dark:text-white">
</div>
<div>
<label for="printer-location" class="block text-sm font-medium text-slate-900 dark:text-white mb-1 sm:mb-2">Standort</label>
<input type="text" id="printer-location" name="location" required
class="w-full px-3 py-2 border border-slate-300 dark:border-slate-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-slate-700 dark:text-white">
</div>
<div>
<label for="printer-mac" class="block text-sm font-medium text-slate-900 dark:text-white mb-1 sm:mb-2">MAC-Adresse</label>
<input type="text" id="printer-mac" name="mac_address" required
placeholder="AA:BB:CC:DD:EE:FF"
class="w-full px-3 py-2 border border-slate-300 dark:border-slate-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-slate-700 dark:text-white">
</div>
<div>
<label for="printer-ip" class="block text-sm font-medium text-slate-900 dark:text-white mb-1 sm:mb-2">Plug IP-Adresse</label>
<input type="text" id="printer-ip" name="plug_ip" required
placeholder="192.168.1.100"
class="w-full px-3 py-2 border border-slate-300 dark:border-slate-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-slate-700 dark:text-white">
</div>
<div>
<label for="printer-plug-username" class="block text-sm font-medium text-slate-900 dark:text-white mb-1 sm:mb-2">Plug Benutzername</label>
<input type="text" id="printer-plug-username" name="plug_username" required
placeholder="admin"
class="w-full px-3 py-2 border border-slate-300 dark:border-slate-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-slate-700 dark:text-white">
</div>
<div>
<label for="printer-plug-password" class="block text-sm font-medium text-slate-900 dark:text-white mb-1 sm:mb-2">Plug Passwort</label>
<input type="password" id="printer-plug-password" name="plug_password" required
placeholder="••••••••"
class="w-full px-3 py-2 border border-slate-300 dark:border-slate-600 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 dark:bg-slate-700 dark:text-white">
</div>
<div class="flex space-x-3 pt-3 sm:pt-4">
<button type="button" id="cancelAddPrinterBtn"
class="flex-1 bg-slate-200 hover:bg-slate-300 text-slate-800 dark:bg-slate-700 dark:hover:bg-slate-600 dark:text-white py-2 px-4 rounded-lg transition-all duration-200 text-sm">
Abbrechen
</button>
<button type="submit"
class="flex-1 bg-green-600 hover:bg-green-700 text-white py-2 px-4 rounded-lg transition-all duration-200 text-sm">
Hinzufügen
</button>
</div>
</form>
</div>
</div>
</div>
{% endif %}
<!-- Printer Detail Modal -->
<div id="printerDetailModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50">
<div class="flex items-center justify-center min-h-screen p-4">
<div class="bg-white dark:bg-slate-800 rounded-xl p-4 sm:p-6 w-full max-w-lg shadow-lg">
<div class="flex items-center justify-between mb-4 sm:mb-6">
<h2 class="text-lg sm:text-xl font-bold text-slate-900 dark:text-white">Drucker Details</h2>
<button id="closePrinterDetailBtn" class="text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white">
<svg class="h-5 w-5 sm:h-6 sm:w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div id="printer-detail-content">
<!-- Content will be loaded here -->
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// Globale Variablen für die Drucker-Verwaltung
let printers = [];
// Modal-Funktionen
function showAddPrinterModal() {
document.getElementById('addPrinterModal').classList.remove('hidden');
}
function hideAddPrinterModal() {
document.getElementById('addPrinterModal').classList.add('hidden');
document.getElementById('addPrinterForm').reset();
}
function showPrinterDetail(printerId) {
const printer = printers.find(p => p.id === printerId);
if (!printer) return;
const content = document.getElementById('printer-detail-content');
content.innerHTML = `
<div class="space-y-3 sm:space-y-4">
<div>
<h3 class="font-medium text-slate-900 dark:text-white text-base sm:text-lg">${printer.name}</h3>
<p class="text-xs sm:text-sm text-slate-600 dark:text-slate-400">${printer.model}</p>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2 sm:gap-4 text-xs sm:text-sm">
<div>
<span class="font-medium text-slate-700 dark:text-slate-300">Status:</span>
<span class="ml-1 sm:ml-2 ${getPrinterStatusColor(printer.status)} px-1.5 sm:px-2 py-0.5 sm:py-1 rounded text-xs">
${getPrinterStatusText(printer.status)}
</span>
</div>
<div>
<span class="font-medium text-slate-700 dark:text-slate-300">Standort:</span>
<span class="ml-1 sm:ml-2 text-slate-900 dark:text-white">${printer.location}</span>
</div>
<div>
<span class="font-medium text-slate-700 dark:text-slate-300">MAC:</span>
<span class="ml-1 sm:ml-2 text-slate-900 dark:text-white font-mono text-xs">${printer.mac_address}</span>
</div>
<div>
<span class="font-medium text-slate-700 dark:text-slate-300">Plug IP:</span>
<span class="ml-1 sm:ml-2 text-slate-900 dark:text-white font-mono text-xs">${printer.plug_ip}</span>
</div>
</div>
<div class="pt-3 sm:pt-4 border-t border-slate-200 dark:border-slate-700">
<p class="text-xs text-slate-600 dark:text-slate-400">
Erstellt: ${formatDate(printer.created_at)}
</p>
</div>
<div class="flex space-x-3 pt-3 sm:pt-4">
<button onclick="hidePrinterDetailModal()"
class="flex-1 bg-slate-200 hover:bg-slate-300 text-slate-800 dark:bg-slate-700 dark:hover:bg-slate-600 dark:text-white py-2 px-4 rounded-lg transition-all duration-200 text-sm">
Schließen
</button>
{% if current_user.is_admin %}
<button onclick="deletePrinter(${printer.id}); hidePrinterDetailModal();"
class="flex-1 bg-red-600 hover:bg-red-700 dark:bg-red-600 dark:hover:bg-red-500 text-white py-2 px-4 rounded-lg transition-all duration-200 text-sm flex items-center justify-center">
<svg class="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
Löschen
</button>
{% endif %}
</div>
</div>
`;
document.getElementById('printerDetailModal').classList.remove('hidden');
}
function hidePrinterDetailModal() {
document.getElementById('printerDetailModal').classList.add('hidden');
}
// Load printers
async function loadPrinters() {
try {
const response = await fetch('/api/printers');
if (!response.ok) {
throw new Error('Fehler beim Laden der Drucker');
}
const data = await response.json();
printers = data.printers || [];
renderPrinters();
} catch (error) {
console.error('Error loading printers:', error);
showError('Fehler beim Laden der Drucker');
}
}
// Render printers grid
function renderPrinters() {
const grid = document.getElementById('printers-grid');
if (printers.length === 0) {
grid.innerHTML = `
<div class="col-span-full text-center py-6 sm:py-12">
<svg class="h-12 w-12 sm:h-16 sm:w-16 text-slate-400 dark:text-slate-500 mx-auto mb-3 sm:mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" />
</svg>
<p class="text-slate-700 dark:text-slate-300 text-base sm:text-lg">Keine Drucker vorhanden</p>
{% if current_user.is_admin %}
<button id="addFirstPrinterBtn" class="mt-3 sm:mt-4 bg-green-600 hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-500 text-white px-4 sm:px-6 py-1.5 sm:py-2 rounded-lg transition-all duration-200 text-sm sm:text-base">
Ersten Drucker hinzufügen
</button>
{% endif %}
</div>
`;
// Event-Listener für den "Ersten Drucker hinzufügen" Button
const addFirstPrinterBtn = document.getElementById('addFirstPrinterBtn');
if (addFirstPrinterBtn) {
addFirstPrinterBtn.addEventListener('click', showAddPrinterModal);
}
return;
}
grid.innerHTML = printers.map(printer => {
const statusColor = getPrinterStatusColor(printer.status);
const statusText = getPrinterStatusText(printer.status);
return `
<div class="bg-white dark:bg-slate-800 rounded-xl p-4 sm:p-6 shadow-sm hover:shadow-lg transition-shadow duration-200 border border-slate-200 dark:border-slate-700">
<div class="flex items-start justify-between mb-3 sm:mb-4">
<div class="flex-1">
<h3 class="text-base sm:text-lg font-bold text-slate-900 dark:text-white">${printer.name}</h3>
<p class="text-xs sm:text-sm text-slate-600 dark:text-slate-400">${printer.model}</p>
</div>
<span class="inline-flex items-center px-2 py-0.5 sm:px-2.5 sm:py-0.5 rounded-full text-xs font-medium ${statusColor}">
${statusText}
</span>
</div>
<div class="space-y-1.5 sm:space-y-2 mb-3 sm:mb-4">
<div class="flex items-center text-xs sm:text-sm text-slate-600 dark:text-slate-400">
<svg class="h-3.5 w-3.5 sm:h-4 sm:w-4 mr-1.5 sm:mr-2 text-slate-500 dark:text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
${printer.location}
</div>
<div class="flex items-center text-xs sm:text-sm text-slate-600 dark:text-slate-400">
<svg class="h-3.5 w-3.5 sm:h-4 sm:w-4 mr-1.5 sm:mr-2 text-slate-500 dark:text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
${printer.mac_address}
</div>
<div class="flex items-center text-xs sm:text-sm text-slate-600 dark:text-slate-400">
<svg class="h-3.5 w-3.5 sm:h-4 sm:w-4 mr-1.5 sm:mr-2 text-slate-500 dark:text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9v-9m0-9v9" />
</svg>
${printer.plug_ip}
</div>
</div>
<div class="flex space-x-2">
<button class="printer-detail-btn flex-1 bg-indigo-600 hover:bg-indigo-700 dark:bg-indigo-600 dark:hover:bg-indigo-500 text-white py-1.5 sm:py-2 px-2 sm:px-3 rounded-lg text-xs sm:text-sm transition-all duration-200" data-printer-id="${printer.id}">
Details
</button>
{% if current_user.is_admin %}
<button class="delete-printer-btn flex-1 bg-red-600 hover:bg-red-700 dark:bg-red-600 dark:hover:bg-red-500 text-white py-1.5 sm:py-2 px-2 sm:px-3 rounded-lg text-xs sm:text-sm transition-all duration-200" data-printer-id="${printer.id}">
<svg class="h-3.5 w-3.5 sm:h-4 sm:w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
Löschen
</button>
{% endif %}
</div>
</div>
`;
}).join('');
// Event-Listener für Details und Löschen-Buttons hinzufügen
document.querySelectorAll('.printer-detail-btn').forEach(btn => {
btn.addEventListener('click', function() {
const printerId = parseInt(this.getAttribute('data-printer-id'));
showPrinterDetail(printerId);
});
});
document.querySelectorAll('.delete-printer-btn').forEach(btn => {
btn.addEventListener('click', function() {
const printerId = parseInt(this.getAttribute('data-printer-id'));
deletePrinter(printerId);
});
});
}
// Helper functions
function getPrinterStatusColor(status) {
switch (status) {
case 'available': return 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300';
case 'busy': return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300';
case 'offline': return 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300';
case 'maintenance': return 'bg-slate-200 text-slate-800 dark:bg-slate-700 dark:text-slate-300';
default: return 'bg-slate-200 text-slate-800 dark:bg-slate-700 dark:text-slate-300';
}
}
function getPrinterStatusText(status) {
switch (status) {
case 'available': return 'Verfügbar';
case 'busy': return 'Beschäftigt';
case 'offline': return 'Offline';
case 'maintenance': return 'Wartung';
default: return 'Unbekannt';
}
}
// Format date helper
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleString('de-DE');
}
// Show error message
function showError(message) {
const grid = document.getElementById('printers-grid');
grid.innerHTML = `
<div class="col-span-full text-center py-6 sm:py-12">
<svg class="h-12 w-12 sm:h-16 sm:w-16 text-red-500 dark:text-red-400 mx-auto mb-3 sm:mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<p class="text-slate-700 dark:text-slate-300 text-base sm:text-lg">${message}</p>
<button onclick="refreshPrinters()" class="mt-3 sm:mt-4 bg-indigo-600 hover:bg-indigo-700 dark:bg-indigo-600 dark:hover:bg-indigo-500 text-white px-4 sm:px-6 py-1.5 sm:py-2 rounded-lg transition-all duration-200 text-sm sm:text-base">
Erneut versuchen
</button>
</div>
`;
}
// Add printer
async function handleAddPrinter(event) {
event.preventDefault();
const form = document.getElementById('addPrinterForm');
const formData = new FormData(form);
const printerData = {
name: formData.get('name'),
model: formData.get('model'),
location: formData.get('location'),
mac_address: formData.get('mac_address'),
plug_ip: formData.get('plug_ip'),
plug_username: formData.get('plug_username'),
plug_password: formData.get('plug_password')
};
try {
const response = await fetch('/api/printers/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')
},
body: JSON.stringify(printerData)
});
if (!response.ok) {
throw new Error('Fehler beim Hinzufügen des Druckers');
}
hideAddPrinterModal();
loadPrinters();
} catch (error) {
console.error('Error adding printer:', error);
alert('Fehler beim Hinzufügen des Druckers');
}
}
// Delete printer
async function deletePrinter(printerId) {
if (!confirm('Sind Sie sicher, dass Sie diesen Drucker löschen möchten?')) {
return;
}
try {
const response = await fetch(`/api/printers/${printerId}`, {
method: 'DELETE',
headers: {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')
}
});
if (!response.ok) {
throw new Error('Fehler beim Löschen des Druckers');
}
hidePrinterDetailModal();
loadPrinters();
} catch (error) {
console.error('Error deleting printer:', error);
alert('Fehler beim Löschen des Druckers');
}
}
// Refresh printers
function refreshPrinters() {
const grid = document.getElementById('printers-grid');
grid.innerHTML = `
<div class="col-span-full text-center py-6 sm:py-12">
<div class="animate-spin rounded-full h-8 w-8 sm:h-12 sm:w-12 border-b-2 border-indigo-600 dark:border-indigo-400 mx-auto"></div>
<p class="mt-3 sm:mt-4 text-sm sm:text-base text-slate-600 dark:text-slate-400">Lade Drucker...</p>
</div>
`;
loadPrinters();
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
// Lade Drucker beim Start
loadPrinters();
// Event-Listener für den "Drucker hinzufügen" Button
const addPrinterBtn = document.getElementById('addPrinterBtn');
if (addPrinterBtn) {
addPrinterBtn.addEventListener('click', showAddPrinterModal);
}
// Event-Listener für das Schließen des Modals
const closeAddPrinterBtn = document.getElementById('closeAddPrinterBtn');
if (closeAddPrinterBtn) {
closeAddPrinterBtn.addEventListener('click', hideAddPrinterModal);
}
const cancelAddPrinterBtn = document.getElementById('cancelAddPrinterBtn');
if (cancelAddPrinterBtn) {
cancelAddPrinterBtn.addEventListener('click', hideAddPrinterModal);
}
// Event-Listener für das Schließen des Detail-Modals
const closePrinterDetailBtn = document.getElementById('closePrinterDetailBtn');
if (closePrinterDetailBtn) {
closePrinterDetailBtn.addEventListener('click', hidePrinterDetailModal);
}
// Event-Listener für das Formular
const addPrinterForm = document.getElementById('addPrinterForm');
if (addPrinterForm) {
addPrinterForm.addEventListener('submit', handleAddPrinter);
}
// Modals schließen, wenn außerhalb geklickt wird
document.getElementById('addPrinterModal').addEventListener('click', function(e) {
if (e.target === this) {
hideAddPrinterModal();
}
});
document.getElementById('printerDetailModal').addEventListener('click', function(e) {
if (e.target === this) {
hidePrinterDetailModal();
}
});
// Modals schließen mit Escape-Taste
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
hideAddPrinterModal();
hidePrinterDetailModal();
}
});
});
</script>
{% endblock %}