"Update database schema for printer functionality (feat)"

This commit is contained in:
2025-05-29 15:11:33 +02:00
parent 486ed41c46
commit 998244db68
3 changed files with 1038 additions and 1088 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -142,102 +142,217 @@
</div>
</div>
<!-- Drucker Detail Modal - Komplett überarbeitet mit expliziter hidden-Klasse -->
<div id="printerDetailModal" class="modal-new hidden" style="display: none !important; visibility: hidden !important;">
<div class="modal-content-new max-w-lg">
<div class="flex items-center justify-between mb-5">
<h2 class="text-xl sm:text-2xl 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 transition-colors">
<svg class="h-5 w-5 sm:h-6 sm:w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<!-- Drucker Detail Modal -->
<div id="printerDetailModal" class="fixed inset-0 bg-black/60 backdrop-blur-sm hidden z-50">
<div class="flex items-center justify-center min-h-screen p-4">
<div class="professional-container max-w-2xl w-full p-8 transform transition-all duration-500 scale-95 opacity-0" id="printerModalContent">
<div class="flex justify-between items-center mb-8">
<div>
<h3 class="text-2xl font-bold text-professional-primary mb-2">Drucker Details</h3>
<p class="text-professional-muted">Detaillierte Informationen zu Ihrem 3D-Drucker</p>
</div>
<button id="closePrinterDetailBtn" class="p-2 hover:bg-slate-100 dark:hover:bg-slate-700 rounded-xl transition-colors duration-200">
<svg class="w-6 h-6 text-professional-muted hover:text-professional-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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 -->
<!-- Inhalt wird dynamisch geladen -->
</div>
</div>
</div>
</div>
<!-- Add Printer Modal - Komplett überarbeitet mit expliziter hidden-Klasse -->
<!-- Add Printer Modal -->
{% if current_user.is_admin %}
<div id="addPrinterModal" class="modal-new hidden" style="display: none !important; visibility: hidden !important;">
<div class="modal-content-new">
<div class="flex items-center justify-between mb-5">
<h2 class="text-xl sm:text-2xl 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 transition-colors">
<svg class="h-5 w-5 sm:h-6 sm:w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<div id="addPrinterModal" class="fixed inset-0 bg-black/60 backdrop-blur-sm hidden z-50">
<div class="flex items-center justify-center min-h-screen p-4">
<div class="professional-container max-w-lg w-full p-8 transform transition-all duration-500 scale-95 opacity-0" id="addPrinterModalContent">
<div class="flex justify-between items-center mb-8">
<div>
<h3 class="text-2xl font-bold text-professional-primary mb-2">Neuen Drucker hinzufügen</h3>
<p class="text-professional-muted">Konfigurieren Sie einen neuen 3D-Drucker</p>
</div>
<button id="closeAddPrinterBtn" class="p-2 hover:bg-slate-100 dark:hover:bg-slate-700 rounded-xl transition-colors duration-200">
<svg class="w-6 h-6 text-professional-muted hover:text-professional-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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-4">
<div>
<label for="printer-name" class="form-label-new">Name</label>
<form id="addPrinterForm" class="space-y-6">
<div class="grid grid-cols-1 gap-6">
<div class="group">
<label for="printer-name" class="block text-sm font-semibold text-professional-primary mb-3">
Drucker Name <span class="text-red-500">*</span>
</label>
<input type="text" id="printer-name" name="name" required
class="input-new" placeholder="z.B. Printer 1">
class="input-professional" placeholder="z.B. Printer 1">
</div>
<div>
<label for="printer-model" class="form-label-new">Modell</label>
<div class="group">
<label for="printer-model" class="block text-sm font-semibold text-professional-primary mb-3">
Modell <span class="text-red-500">*</span>
</label>
<input type="text" id="printer-model" name="model" required
class="input-new" placeholder="z.B. P115">
class="input-professional" placeholder="z.B. P115">
</div>
<div>
<label for="printer-location" class="form-label-new">Standort</label>
<div class="group">
<label for="printer-location" class="block text-sm font-semibold text-professional-primary mb-3">
Standort <span class="text-red-500">*</span>
</label>
<input type="text" id="printer-location" name="location" required
class="input-new" placeholder="z.B. Werk 040 - Berlin - TBA">
class="input-professional" placeholder="z.B. Werk 040 - Berlin - TBA">
</div>
<div>
<label for="printer-mac" class="form-label-new">MAC-Adresse</label>
<div class="group">
<label for="printer-mac" class="block text-sm font-semibold text-professional-primary mb-3">
MAC-Adresse <span class="text-red-500">*</span>
</label>
<input type="text" id="printer-mac" name="mac_address" required
placeholder="AA:BB:CC:DD:EE:FF"
class="input-new">
class="input-professional font-mono">
</div>
<div>
<label for="printer-ip" class="form-label-new">Plug IP-Adresse</label>
<div class="group">
<label for="printer-ip" class="block text-sm font-semibold text-professional-primary mb-3">
Plug IP-Adresse <span class="text-red-500">*</span>
</label>
<input type="text" id="printer-ip" name="plug_ip" required
placeholder="192.168.1.100"
class="input-new">
class="input-professional font-mono">
</div>
<div>
<label for="printer-plug-username" class="form-label-new">Plug Benutzername</label>
<div class="group">
<label for="printer-plug-username" class="block text-sm font-semibold text-professional-primary mb-3">
Plug Benutzername <span class="text-red-500">*</span>
</label>
<input type="text" id="printer-plug-username" name="plug_username" required
placeholder="admin"
class="input-new">
class="input-professional">
</div>
<div>
<label for="printer-plug-password" class="form-label-new">Plug Passwort</label>
<div class="group">
<label for="printer-plug-password" class="block text-sm font-semibold text-professional-primary mb-3">
Plug Passwort <span class="text-red-500">*</span>
</label>
<input type="password" id="printer-plug-password" name="plug_password" required
placeholder="••••••••"
class="input-new">
class="input-professional">
</div>
</div>
<div class="flex gap-3 pt-3">
<div class="flex items-center justify-end space-x-4 pt-8 border-t border-slate-200 dark:border-slate-600">
<button type="button" id="cancelAddPrinterBtn"
class="flex-1 action-btn-new bg-slate-200 hover:bg-slate-300 text-slate-800 dark:bg-slate-700 dark:hover:bg-slate-600 dark:text-white">
class="btn-secondary-professional">
Abbrechen
</button>
<button type="submit"
class="flex-1 action-btn-new success">
Hinzufügen
class="btn-success-professional group">
<svg class="w-5 h-5 mr-3 group-hover:scale-110 transition-transform duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/>
</svg>
<span>Drucker hinzufügen</span>
</button>
</div>
</form>
</div>
</div>
</div>
{% endif %}
{% endblock %}
{% block scripts %}
<script>
// Live Time Update
function updateLiveTime() {
const now = new Date();
const timeElement = document.getElementById('live-time');
if (timeElement) {
timeElement.textContent = now.toLocaleTimeString('de-DE');
}
}
updateLiveTime();
setInterval(updateLiveTime, 1000);
// Filter Button Styling
function setupFilterButtons() {
const filterButtons = document.querySelectorAll('.filter-btn-mercedes');
filterButtons.forEach(btn => {
btn.addEventListener('click', function() {
// Remove active class from all buttons
filterButtons.forEach(b => {
b.classList.remove('active');
b.classList.remove('bg-professional-accent', 'text-white');
b.classList.add('text-professional-muted', 'hover:text-professional-primary');
});
// Add active class to clicked button
this.classList.add('active');
this.classList.remove('text-professional-muted', 'hover:text-professional-primary');
this.classList.add('bg-professional-accent', 'text-white');
// Update filter
const filterId = this.id;
if (filterId === 'filter-all') {
currentFilter = 'all';
} else if (filterId === 'filter-online') {
currentFilter = 'online';
} else if (filterId === 'filter-offline') {
currentFilter = 'offline';
}
// Re-render printers with new filter
renderPrinters();
});
});
}
// Modal Animations - Professional Mercedes Style
function showModal(modalId) {
const modal = document.getElementById(modalId);
const content = modal.querySelector('.professional-container');
if (!modal || !content) {
console.error('Modal or content not found:', modalId);
return;
}
modal.classList.remove('hidden');
modal.style.display = '';
modal.style.visibility = '';
modal.setAttribute('aria-hidden', 'false');
setTimeout(() => {
content.classList.remove('scale-95', 'opacity-0');
content.classList.add('scale-100', 'opacity-100');
}, 10);
}
function hideModal(modalId) {
const modal = document.getElementById(modalId);
const content = modal.querySelector('.professional-container');
if (!modal || !content) {
console.error('Modal or content not found:', modalId);
return;
}
content.classList.remove('scale-100', 'opacity-100');
content.classList.add('scale-95', 'opacity-0');
setTimeout(() => {
modal.classList.add('hidden');
modal.style.display = 'none';
modal.style.visibility = 'hidden';
modal.setAttribute('aria-hidden', 'true');
}, 200);
}
// Globale Variablen für die Drucker-Verwaltung
let printers = [];
let currentFilter = 'all';
@@ -251,7 +366,7 @@
console.log("DOM vollständig geladen, initialisiere Seite...");
// WICHTIG: Sofort alle Modals verstecken (mit mehreren Methoden)
const modalElements = document.querySelectorAll('.modal-new');
const modalElements = document.querySelectorAll('[id$="Modal"]');
modalElements.forEach(modal => {
// Mehrere Ansätze kombinieren für maximale Sicherheit
modal.classList.add('hidden');
@@ -270,7 +385,7 @@
console.log("Initialisiere Drucker-Seite...");
// Filter-Buttons einrichten
setupFilters();
setupFilterButtons();
// Event-Listener für Buttons und Modals initialisieren
setupEventListeners();
@@ -336,24 +451,6 @@
hideAllModals();
}
});
// Refresh-Button
const refreshBtn = document.querySelector('button[onclick="refreshPrinters()"]');
if (refreshBtn) {
refreshBtn.addEventListener('click', function(e) {
e.preventDefault();
refreshPrinters();
});
}
// Auto-Refresh-Button
const autoRefreshBtn = document.getElementById('auto-refresh-btn');
if (autoRefreshBtn) {
autoRefreshBtn.addEventListener('click', function(e) {
e.preventDefault();
toggleAutoRefresh();
});
}
}
// Hilfsfunktion für Schließen-Buttons
@@ -390,135 +487,25 @@
// Modal-Steuerungsfunktionen
function showAddPrinterModal() {
const modal = document.getElementById('addPrinterModal');
if (!modal) return;
console.log("Öffne 'Drucker hinzufügen' Modal");
modal.classList.remove('hidden');
modal.style.display = '';
modal.style.visibility = '';
modal.setAttribute('aria-hidden', 'false');
showModal('addPrinterModal');
}
function hideAddPrinterModal() {
const modal = document.getElementById('addPrinterModal');
if (!modal) return;
console.log("Schließe 'Drucker hinzufügen' Modal");
modal.classList.add('hidden');
modal.style.display = 'none';
modal.style.visibility = 'hidden';
modal.setAttribute('aria-hidden', 'true');
hideModal('addPrinterModal');
// Formular zurücksetzen
const form = document.getElementById('addPrinterForm');
if (form) form.reset();
}
function showPrinterDetail(printerId) {
// Drucker-Details finden
const printer = printers.find(p => p.id === printerId);
if (!printer) {
console.error("Drucker nicht gefunden:", printerId);
return;
}
// Detail-Inhalt aktualisieren
const content = document.getElementById('printer-detail-content');
if (!content) {
console.error("printer-detail-content Element nicht gefunden");
return;
}
// Prüfen, ob das Modal existiert
const modal = document.getElementById('printerDetailModal');
if (!modal) {
console.error("printerDetailModal Element nicht gefunden");
return;
}
// Modal-Inhalt mit Drucker-Daten füllen
const isOnline = printer.status === 'available' || printer.is_online;
content.innerHTML = `
<div class="space-y-4">
<div class="${isOnline ? 'bg-green-50/80 dark:bg-green-900/30' : 'bg-slate-50/80 dark:bg-slate-700/30'} rounded-lg p-3 sm:p-4 backdrop-blur-sm border ${isOnline ? 'border-green-200 dark:border-green-800/30' : 'border-slate-200 dark:border-slate-600/30'}">
<h3 class="font-semibold ${isOnline ? 'text-green-800 dark:text-green-300' : 'text-slate-900 dark:text-white'} text-lg mb-1">${printer.name}</h3>
<p class="text-sm ${isOnline ? 'text-green-700 dark:text-green-400' : 'text-slate-600 dark:text-slate-400'}">${printer.model}</p>
<div class="mt-3 flex items-center gap-2">
<span class="status-badge-new ${isOnline ? 'online' : 'offline'}">
${isOnline ? '🟢' : '🔴'} ${getPrinterStatusText(printer.status)}
</span>
${printer.last_checked ?
`<span class="text-xs ${isOnline ? 'text-green-600 dark:text-green-400' : 'text-slate-500 dark:text-slate-400'}">
Zuletzt geprüft: ${formatDate(printer.last_checked)}
</span>` : ''}
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
<div class="bg-white/60 dark:bg-slate-800/60 rounded-lg p-3 backdrop-blur-sm border border-slate-200/60 dark:border-slate-700/30">
<div class="text-xs uppercase font-semibold text-slate-500 dark:text-slate-400 mb-1.5">Standort</div>
<div class="text-sm text-slate-900 dark:text-white">${printer.location}</div>
</div>
<div class="bg-white/60 dark:bg-slate-800/60 rounded-lg p-3 backdrop-blur-sm border border-slate-200/60 dark:border-slate-700/30">
<div class="text-xs uppercase font-semibold text-slate-500 dark:text-slate-400 mb-1.5">MAC-Adresse</div>
<div class="text-sm text-slate-900 dark:text-white font-mono">${printer.mac_address}</div>
</div>
<div class="bg-white/60 dark:bg-slate-800/60 rounded-lg p-3 backdrop-blur-sm border border-slate-200/60 dark:border-slate-700/30">
<div class="text-xs uppercase font-semibold text-slate-500 dark:text-slate-400 mb-1.5">Plug IP</div>
<div class="text-sm text-slate-900 dark:text-white font-mono">${printer.plug_ip}</div>
</div>
<div class="bg-white/60 dark:bg-slate-800/60 rounded-lg p-3 backdrop-blur-sm border border-slate-200/60 dark:border-slate-700/30">
<div class="text-xs uppercase font-semibold text-slate-500 dark:text-slate-400 mb-1.5">Erstellt am</div>
<div class="text-sm text-slate-900 dark:text-white">${formatDate(printer.created_at)}</div>
</div>
</div>
<div class="border-t border-slate-200 dark:border-slate-700 pt-4 mt-4">
<div class="flex gap-3">
<button onclick="hidePrinterDetailModal()"
class="flex-1 action-btn-new bg-slate-200 hover:bg-slate-300 text-slate-800 dark:bg-slate-700 dark:hover:bg-slate-600 dark:text-white">
Schließen
</button>
{% if current_user.is_admin %}
<button onclick="deletePrinter(${printer.id}); hidePrinterDetailModal();"
class="flex-1 action-btn-new danger">
<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>
</div>
`;
// Modal anzeigen
console.log("Öffne Drucker-Details Modal für Drucker:", printerId);
modal.classList.remove('hidden');
modal.style.display = '';
modal.style.visibility = '';
modal.setAttribute('aria-hidden', 'false');
}
function hidePrinterDetailModal() {
const modal = document.getElementById('printerDetailModal');
if (!modal) return;
console.log("Schließe Drucker-Details Modal");
modal.classList.add('hidden');
modal.style.display = 'none';
modal.style.visibility = 'hidden';
modal.setAttribute('aria-hidden', 'true');
hideModal('printerDetailModal');
}
// Render printers grid mit Filter-Unterstützung
// Render printers grid mit Filter-Unterstützung - Mercedes Professional Style
function renderPrinters() {
const grid = document.getElementById('printers-grid');
if (!grid) {
@@ -550,15 +537,22 @@
}
grid.innerHTML = `
<div class="col-span-full text-center py-8 sm:py-12">
<svg class="h-14 w-14 sm:h-16 sm:w-16 text-slate-300 dark:text-slate-600 mx-auto mb-4 sm:mb-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<div class="col-span-full text-center py-12">
<div class="mb-glass rounded-3xl p-12 max-w-md mx-auto">
<svg class="h-16 w-16 text-professional-muted mx-auto mb-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" 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 font-medium">${emptyMessage}</p>
<h3 class="text-xl font-bold text-professional-primary mb-3">${emptyMessage}</h3>
<p class="text-professional-muted mb-6">
${currentFilter === 'all' && printers.length === 0 ?
'Fügen Sie Ihren ersten 3D-Drucker hinzu, um loszulegen.' :
'Versuchen Sie einen anderen Filter oder aktualisieren Sie die Ansicht.'}
</p>
${currentFilter === 'all' && printers.length === 0 ? `
{% if current_user.is_admin %}
<button id="addFirstPrinterBtn" class="mt-4 sm:mt-5 action-btn-new success">
<svg class="h-4 w-4 sm:h-5 sm:w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<button id="addFirstPrinterBtn" class="btn-success-professional group">
<svg class="w-6 h-6 mr-3 group-hover:scale-110 transition-transform duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/>
</svg>
<span>Ersten Drucker hinzufügen</span>
@@ -566,16 +560,13 @@
{% endif %}
` : ''}
</div>
</div>
`;
// Event-Listener für den "Ersten Drucker hinzufügen" Button
const addFirstPrinterBtn = document.getElementById('addFirstPrinterBtn');
if (addFirstPrinterBtn) {
// Entferne vorherige Event-Listener, um doppelte Registrierung zu vermeiden
const clonedBtn = addFirstPrinterBtn.cloneNode(true);
addFirstPrinterBtn.parentNode.replaceChild(clonedBtn, addFirstPrinterBtn);
clonedBtn.addEventListener('click', function(e) {
addFirstPrinterBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
showAddPrinterModal();
@@ -590,99 +581,81 @@
const statusText = getPrinterStatusText(printer.status);
return `
<div class="printer-card-new ${isOnline ? 'online' : ''}">
${isOnline ? '<div class="online-indicator"></div>' : ''}
<div class="mb-glass p-6 rounded-3xl border border-white/20 dark:border-white/10 hover:border-professional-accent/50 dark:hover:border-professional-accent/30 transition-all duration-300 group cursor-pointer transform hover:scale-105 ${isOnline ? 'ring-2 ring-green-500/20 dark:ring-green-400/20' : ''}" onclick="showPrinterDetail(${printer.id})">
<div class="flex items-start justify-between mb-4">
<div>
<h3 class="text-base sm:text-lg font-bold ${isOnline ? 'text-green-800 dark:text-green-300' : 'text-slate-900 dark:text-white'}">${printer.name}</h3>
<p class="text-xs sm:text-sm ${isOnline ? 'text-green-700 dark:text-green-400' : 'text-slate-600 dark:text-slate-400'}">${printer.model}</p>
</div>
<div class="flex flex-col items-end">
<span class="status-badge-new ${isOnline ? 'online' : 'offline'}">
<!-- Status Indicator -->
${isOnline ? '<div class="absolute -top-2 -right-2 w-6 h-6 bg-green-500 rounded-full border-4 border-white dark:border-slate-800 animate-pulse"></div>' : ''}
<!-- Header -->
<div class="flex items-start justify-between mb-6">
<div class="flex-1">
<h3 class="text-xl font-bold text-professional-primary mb-2 group-hover:text-professional-accent transition-colors duration-300">${printer.name}</h3>
<p class="text-professional-secondary mb-3">${printer.model}</p>
<!-- Status Badge -->
<div class="inline-flex items-center px-3 py-1 rounded-full text-sm font-semibold ${isOnline ? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300' : 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300'}">
${isOnline ? '🟢' : '🔴'} ${statusText}
</span>
${printer.last_checked ?
`<span class="text-xs ${isOnline ? 'text-green-600 dark:text-green-400' : 'text-slate-500 dark:text-slate-400'} mt-1.5">
Geprüft: ${formatTime(printer.last_checked)}
</span>` : ''}
</div>
</div>
<div class="space-y-2 mb-4">
<div class="printer-info-row">
<svg class="printer-info-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<!-- Action Icon -->
<div class="w-10 h-10 bg-gradient-to-br from-professional-accent/20 to-professional-primary/20 rounded-xl flex items-center justify-center group-hover:from-professional-accent/30 group-hover:to-professional-primary/30 transition-all duration-300">
<svg class="w-5 h-5 text-professional-accent group-hover:scale-110 transition-transform duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
</div>
<!-- Details -->
<div class="space-y-3 mb-6">
<div class="flex items-center space-x-3 text-sm text-professional-secondary">
<svg class="w-4 h-4 text-professional-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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>
<span>${printer.location}</span>
<span class="truncate">${printer.location}</span>
</div>
<div class="printer-info-row">
<svg class="printer-info-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<div class="flex items-center space-x-3 text-sm text-professional-muted">
<svg class="w-4 h-4 text-professional-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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>
<span class="font-mono text-xs">${printer.mac_address}</span>
</div>
<div class="printer-info-row">
<svg class="printer-info-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<div class="flex items-center space-x-3 text-sm text-professional-muted">
<svg class="w-4 h-4 text-professional-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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>
<span class="font-mono text-xs">${printer.plug_ip}</span>
</div>
${printer.last_checked ?
`<div class="flex items-center space-x-3 text-xs text-professional-muted">
<svg class="w-4 h-4 text-professional-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<span>Geprüft: ${formatTime(printer.last_checked)}</span>
</div>` : ''}
</div>
<div class="flex gap-2">
<button class="printer-detail-btn flex-1 action-btn-new primary text-xs" data-printer-id="${printer.id}">
Details
</button>
<!-- Actions -->
<div class="flex gap-3">
<div class="flex-1 btn-professional-mini text-center opacity-80 group-hover:opacity-100 transition-opacity duration-300">
Details anzeigen
</div>
{% if current_user.is_admin %}
<button class="delete-printer-btn flex-1 action-btn-new danger text-xs" 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">
<button class="btn-danger-professional-mini" onclick="event.stopPropagation(); deletePrinter(${printer.id});">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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('');
// Sichere Ereignisbehandlung: Alte Event-Listener entfernen, indem wir neue Listener hinzufügen
document.querySelectorAll('.printer-detail-btn').forEach(btn => {
// Entferne alte Listener durch Klon-Ersetzung
const clonedBtn = btn.cloneNode(true);
btn.parentNode.replaceChild(clonedBtn, btn);
// Füge neuen Listener hinzu
clonedBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const printerId = parseInt(this.getAttribute('data-printer-id'));
if (printerId) {
showPrinterDetail(printerId);
}
});
});
document.querySelectorAll('.delete-printer-btn').forEach(btn => {
// Entferne alte Listener durch Klon-Ersetzung
const clonedBtn = btn.cloneNode(true);
btn.parentNode.replaceChild(clonedBtn, btn);
// Füge neuen Listener hinzu
clonedBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const printerId = parseInt(this.getAttribute('data-printer-id'));
if (printerId) {
deletePrinter(printerId);
}
});
});
}
// Helper functions
@@ -1130,29 +1103,6 @@
}
}
// Filter-Funktionalität
function setupFilters() {
const filterButtons = document.querySelectorAll('.filter-btn-mercedes');
filterButtons.forEach(btn => {
btn.addEventListener('click', function() {
// Entferne active-Klasse von allen Buttons
filterButtons.forEach(b => {
b.classList.remove('active');
});
// Füge active-Klasse zum geklickten Button hinzu
this.classList.add('active');
// Setze aktuellen Filter
currentFilter = this.id.replace('filter-', '');
// Rendere Drucker mit Filter
renderPrinters();
});
});
}
// Delete printer
async function deletePrinter(printerId) {
if (!confirm('Sind Sie sicher, dass Sie diesen Drucker löschen möchten?')) {