Files
Projektarbeit-MYP/backend/app/templates/printers.html

1523 lines
70 KiB
HTML

{% extends "base.html" %}
{% block title %}3D-Drucker - Mercedes-Benz MYP Platform{% endblock %}
{% block extra_css %}
<style>
/* Mercedes-Benz Corporate Design */
.text-mercedes-black { color: #000000; }
.text-mercedes-gray { color: #6b7280; }
.text-mercedes-silver { color: #9ca3af; }
.text-mercedes-blue { color: #0073ce; }
.text-mercedes-green { color: #008c32; }
.text-mercedes-red { color: #dc2626; }
.bg-mercedes-black { background-color: #000000; }
.bg-mercedes-silver { background-color: #e5e7eb; }
.bg-mercedes-blue { background-color: #0073ce; }
.bg-mercedes-green { background-color: #008c32; }
.border-mercedes-silver { border-color: #d1d5db; }
.border-mercedes-blue { border-color: #0073ce; }
.hover\:border-mercedes-blue:hover { border-color: #0073ce; }
.focus\:ring-mercedes-blue:focus {
--tw-ring-color: #0073ce;
--tw-ring-opacity: 0.5;
}
.focus\:border-mercedes-blue:focus { border-color: #0073ce; }
/* Enhanced Printer Cards */
.printer-card {
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
border: 1px solid #e2e8f0;
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -1px rgba(0, 0, 0, 0.06);
border-radius: 16px;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.dark .printer-card {
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
border-color: #334155;
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.3),
0 2px 4px -1px rgba(0, 0, 0, 0.2);
}
.printer-card:hover {
transform: translateY(-4px);
box-shadow:
0 20px 25px -5px rgba(0, 0, 0, 0.1),
0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.dark .printer-card:hover {
box-shadow:
0 20px 25px -5px rgba(0, 0, 0, 0.3),
0 10px 10px -5px rgba(0, 0, 0, 0.2);
}
/* Status Indicators */
.status-online {
border-left: 4px solid #10b981;
background: linear-gradient(90deg, rgba(16, 185, 129, 0.1) 0%, rgba(5, 150, 105, 0.05) 100%);
}
.status-offline {
border-left: 4px solid #ef4444;
background: linear-gradient(90deg, rgba(239, 68, 68, 0.1) 0%, rgba(220, 38, 38, 0.05) 100%);
}
.status-printing {
border-left: 4px solid #3b82f6;
background: linear-gradient(90deg, rgba(59, 130, 246, 0.1) 0%, rgba(37, 99, 235, 0.05) 100%);
}
.status-error {
border-left: 4px solid #f59e0b;
background: linear-gradient(90deg, rgba(245, 158, 11, 0.1) 0%, rgba(217, 119, 6, 0.05) 100%);
}
.status-maintenance {
border-left: 4px solid #8b5cf6;
background: linear-gradient(90deg, rgba(139, 92, 246, 0.1) 0%, rgba(124, 58, 237, 0.05) 100%);
}
/* Real-time Status Indicators */
.status-pulse {
animation: pulse-status 2s infinite;
}
@keyframes pulse-status {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.8; transform: scale(1.1); }
}
/* Temperature Gauges */
.temperature-gauge {
width: 80px;
height: 80px;
position: relative;
background: conic-gradient(from 0deg, #ef4444 0%, #f59e0b 25%, #10b981 50%, #3b82f6 75%, #ef4444 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.temperature-gauge::before {
content: '';
position: absolute;
width: 70px;
height: 70px;
background: #ffffff;
border-radius: 50%;
top: 5px;
left: 5px;
}
.dark .temperature-gauge::before {
background: #1e293b;
}
.temperature-value {
position: relative;
z-index: 1;
font-weight: bold;
font-size: 0.875rem;
text-align: center;
}
/* Progress Indicators */
.print-progress {
background: #f3f4f6;
height: 8px;
border-radius: 4px;
overflow: hidden;
position: relative;
}
.dark .print-progress {
background: #374151;
}
.print-progress-bar {
background: linear-gradient(90deg, #0073ce 0%, #1e40af 100%);
height: 100%;
border-radius: 4px;
transition: width 0.8s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.dark .print-progress-bar {
background: linear-gradient(90deg, #60a5fa 0%, #3b82f6 100%);
}
.print-progress-bar::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
animation: progress-shine 2s infinite;
}
@keyframes progress-shine {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
/* Enhanced Form Elements */
.mercedes-form-input {
transition: all 0.2s ease;
border: 1px solid #d1d5db;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
}
.mercedes-form-input:focus {
border-color: #0073ce;
box-shadow:
0 0 0 3px rgba(0, 115, 206, 0.1),
0 4px 6px -1px rgba(0, 0, 0, 0.1);
transform: translateY(-1px);
background: rgba(255, 255, 255, 1);
}
.dark .mercedes-form-input {
background: rgba(30, 41, 59, 0.8);
border-color: #475569;
color: #f8fafc;
}
.dark .mercedes-form-input:focus {
border-color: #0ea5e9;
box-shadow:
0 0 0 3px rgba(14, 165, 233, 0.1),
0 4px 6px -1px rgba(0, 0, 0, 0.3);
background: rgba(30, 41, 59, 1);
}
/* Enhanced Modals */
.mercedes-modal {
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
border: 1px solid #e2e8f0;
box-shadow:
0 25px 50px -12px rgba(0, 0, 0, 0.25),
0 4px 6px -1px rgba(0, 0, 0, 0.1);
border-radius: 16px;
backdrop-filter: blur(10px);
}
.dark .mercedes-modal {
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
border-color: #334155;
box-shadow:
0 25px 50px -12px rgba(0, 0, 0, 0.5),
0 4px 6px -1px rgba(0, 0, 0, 0.3);
}
/* Action Buttons */
.printer-action-btn {
padding: 0.5rem 1rem;
border-radius: 8px;
font-weight: 500;
font-size: 0.875rem;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
gap: 0.5rem;
border: none;
cursor: pointer;
}
.printer-action-btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.btn-start {
background: #16a34a;
color: white;
}
.btn-stop {
background: #ef4444;
color: white;
}
.btn-pause {
background: #f59e0b;
color: white;
}
.btn-maintenance {
background: #8b5cf6;
color: white;
}
.btn-details {
background: #0073ce;
color: white;
}
.btn-edit {
background: #6b7280;
color: white;
}
/* Live Monitoring Dashboard */
.monitoring-widget {
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 1rem;
transition: all 0.3s ease;
}
.dark .monitoring-widget {
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
border-color: #334155;
}
.monitoring-widget:hover {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
transform: translateY(-1px);
}
/* Statistics Cards */
.stat-card {
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
border: 1px solid #e2e8f0;
border-radius: 12px;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.dark .stat-card {
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
border-color: #334155;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.stat-card.online {
border-left: 4px solid #10b981;
}
.stat-card.offline {
border-left: 4px solid #ef4444;
}
.stat-card.printing {
border-left: 4px solid #3b82f6;
}
.stat-card.total {
border-left: 4px solid #6b7280;
}
/* Loading States */
.loading-skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 2s infinite;
border-radius: 8px;
}
.dark .loading-skeleton {
background: linear-gradient(90deg, #374151 25%, #4b5563 50%, #374151 75%);
background-size: 200% 100%;
}
@keyframes loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* Filter Section */
.filter-section {
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 1.5rem;
transition: all 0.3s ease;
}
.dark .filter-section {
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
border-color: #334155;
}
.filter-section:hover {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
/* Network Status Indicators */
.network-indicator {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 500;
}
.network-connected {
background: rgba(16, 185, 129, 0.1);
color: #059669;
border: 1px solid rgba(16, 185, 129, 0.2);
}
.network-disconnected {
background: rgba(239, 68, 68, 0.1);
color: #dc2626;
border: 1px solid rgba(239, 68, 68, 0.2);
}
/* Responsive Design */
@media (max-width: 768px) {
.printer-card {
margin: 0 0.5rem;
}
.filter-section {
padding: 1rem;
}
.printer-action-btn {
font-size: 0.75rem;
padding: 0.375rem 0.75rem;
}
.temperature-gauge {
width: 60px;
height: 60px;
}
.temperature-gauge::before {
width: 50px;
height: 50px;
top: 5px;
left: 5px;
}
}
/* Accessibility Enhancements */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Focus States */
.printer-card:focus-within {
outline: 2px solid #0073ce;
outline-offset: 2px;
}
.printer-action-btn:focus {
outline: 2px solid #0073ce;
outline-offset: 2px;
}
</style>
{% endblock %}
{% block head %}
{{ super() }}
<meta name="csrf-token" content="{{ csrf_token() }}">
{% endblock %}
{% block content %}
<div class="space-y-8">
<!-- Enhanced Page Header -->
<div class="dashboard-card p-6">
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-6">
<div class="flex items-center gap-6">
<div class="w-16 h-16 flex-shrink-0 bg-mercedes-blue text-white rounded-xl flex items-center justify-center">
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"/>
</svg>
</div>
<div>
<h1 class="text-4xl font-bold text-mercedes-black dark:text-white tracking-tight">3D-Drucker</h1>
<p class="text-mercedes-gray dark:text-slate-400 mt-1 text-lg">Live-Überwachung und Verwaltung Ihrer Produktionseinheiten</p>
<div class="flex items-center mt-2 text-sm text-mercedes-blue">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
<span id="last-update">Live-Updates aktiv</span>
</div>
</div>
</div>
<div class="flex flex-wrap gap-3">
<button onclick="refreshPrinters()" id="refresh-button"
class="btn-secondary flex items-center gap-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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>
<span>Aktualisieren</span>
</button>
<button onclick="toggleMaintenanceMode()" id="maintenance-toggle"
class="btn-secondary flex items-center gap-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
<span>Wartungsmodus</span>
</button>
<button onclick="openAddPrinterModal()"
class="btn-primary flex items-center gap-2">
<svg class="w-5 h-5" 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>
</div>
</div>
<!-- Enhanced Statistics Cards -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<div class="stat-card online p-6">
<div class="flex justify-between items-center">
<div>
<h3 class="text-sm font-medium text-mercedes-gray dark:text-slate-400 mb-2">Online</h3>
<div class="text-3xl font-bold text-green-600 dark:text-green-400 mb-1" id="online-count">-</div>
<div class="flex items-center text-xs text-green-600 dark:text-green-400">
<div class="w-2 h-2 bg-green-500 rounded-full mr-2 status-pulse"></div>
<span>Betriebsbereit</span>
</div>
</div>
<div class="w-12 h-12 bg-green-100 dark:bg-green-900/30 rounded-xl flex items-center justify-center">
<div class="w-6 h-6 bg-green-500 rounded-full"></div>
</div>
</div>
<div class="mt-4 pt-4 border-t border-green-200 dark:border-green-800">
<div class="text-xs text-mercedes-gray dark:text-slate-400">
<span class="font-medium" id="online-percentage">0%</span> aller Drucker
</div>
</div>
</div>
<div class="stat-card offline p-6">
<div class="flex justify-between items-center">
<div>
<h3 class="text-sm font-medium text-mercedes-gray dark:text-slate-400 mb-2">Offline</h3>
<div class="text-3xl font-bold text-red-600 dark:text-red-400 mb-1" id="offline-count">-</div>
<div class="flex items-center text-xs text-red-600 dark:text-red-400">
<div class="w-2 h-2 bg-red-500 rounded-full mr-2"></div>
<span>Nicht verfügbar</span>
</div>
</div>
<div class="w-12 h-12 bg-red-100 dark:bg-red-900/30 rounded-xl flex items-center justify-center">
<div class="w-6 h-6 bg-red-500 rounded-full"></div>
</div>
</div>
<div class="mt-4 pt-4 border-t border-red-200 dark:border-red-800">
<div class="text-xs text-mercedes-gray dark:text-slate-400">
<span class="font-medium" id="offline-percentage">0%</span> aller Drucker
</div>
</div>
</div>
<div class="stat-card printing p-6">
<div class="flex justify-between items-center">
<div>
<h3 class="text-sm font-medium text-mercedes-gray dark:text-slate-400 mb-2">Aktiv</h3>
<div class="text-3xl font-bold text-blue-600 dark:text-blue-400 mb-1" id="printing-count">-</div>
<div class="flex items-center text-xs text-blue-600 dark:text-blue-400">
<div class="w-2 h-2 bg-blue-500 rounded-full mr-2 status-pulse"></div>
<span>Druckt gerade</span>
</div>
</div>
<div class="w-12 h-12 bg-blue-100 dark:bg-blue-900/30 rounded-xl flex items-center justify-center">
<div class="w-6 h-6 bg-blue-500 rounded-full animate-pulse"></div>
</div>
</div>
<div class="mt-4 pt-4 border-t border-blue-200 dark:border-blue-800">
<div class="text-xs text-mercedes-gray dark:text-slate-400">
<span class="font-medium" id="active-percentage">0%</span> Auslastung
</div>
</div>
</div>
<div class="stat-card total p-6">
<div class="flex justify-between items-center">
<div>
<h3 class="text-sm font-medium text-mercedes-gray dark:text-slate-400 mb-2">Gesamt</h3>
<div class="text-3xl font-bold text-mercedes-black dark:text-white mb-1" id="total-count">-</div>
<div class="flex items-center text-xs text-mercedes-gray dark:text-slate-400">
<svg class="w-3 h-3 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"/>
</svg>
<span>Drucker registriert</span>
</div>
</div>
<div class="w-12 h-12 bg-mercedes-silver rounded-xl flex items-center justify-center">
<svg class="w-6 h-6 text-mercedes-black dark:text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"/>
</svg>
</div>
</div>
<div class="mt-4 pt-4 border-t border-mercedes-silver">
<div class="text-xs text-mercedes-gray dark:text-slate-400">
Letzte Aktualisierung: <span id="last-update-time">--:--</span>
</div>
</div>
</div>
</div>
<!-- Live Performance Overview -->
<div class="dashboard-card p-6">
<div class="flex items-center justify-between mb-6">
<div>
<h2 class="text-2xl font-bold text-mercedes-black dark:text-white mb-2">Live Performance</h2>
<p class="text-mercedes-gray dark:text-slate-400">Echzeit-Überwachung der Druckerleistung</p>
</div>
<div class="flex items-center gap-3">
<div class="network-indicator network-connected">
<div class="w-2 h-2 bg-green-500 rounded-full status-pulse"></div>
<span>Verbunden</span>
</div>
<button onclick="toggleAutoRefresh()" id="auto-refresh-toggle"
class="text-sm px-3 py-1 bg-mercedes-blue text-white rounded-lg hover:bg-blue-700 transition-colors">
Auto-Refresh: ON
</button>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="monitoring-widget text-center">
<div class="text-sm font-medium text-mercedes-gray dark:text-slate-400 mb-2">Durchschnittliche Auslastung</div>
<div class="text-2xl font-bold text-mercedes-blue mb-2" id="avg-utilization">--</div>
<div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
<div class="bg-mercedes-blue h-2 rounded-full transition-all duration-1000"
id="utilization-bar" style="width: 0%"></div>
</div>
</div>
<div class="monitoring-widget text-center">
<div class="text-sm font-medium text-mercedes-gray dark:text-slate-400 mb-2">Aktive Jobs</div>
<div class="text-2xl font-bold text-green-600 mb-2" id="active-jobs-count">--</div>
<div class="text-xs text-mercedes-gray dark:text-slate-400">
Geschätzte Fertigstellung: <span id="estimated-completion">--:--</span>
</div>
</div>
<div class="monitoring-widget text-center">
<div class="text-sm font-medium text-mercedes-gray dark:text-slate-400 mb-2">Warteschlange</div>
<div class="text-2xl font-bold text-yellow-600 mb-2" id="queue-count">--</div>
<div class="text-xs text-mercedes-gray dark:text-slate-400">
Nächster Start: <span id="next-job-start">--:--</span>
</div>
</div>
</div>
</div>
<!-- Enhanced Filter Section -->
<div class="filter-section">
<div class="flex items-center justify-between mb-6">
<div>
<h3 class="text-lg font-semibold text-mercedes-black dark:text-white mb-2">Filter & Suche</h3>
<p class="text-sm text-mercedes-gray dark:text-slate-400">Finden Sie schnell den gewünschten Drucker</p>
</div>
<button onclick="clearAllFilters()" class="text-sm text-mercedes-blue hover:text-blue-700 transition-colors">
<svg class="w-4 h-4 inline mr-1" 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>
Alle Filter zurücksetzen
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<!-- Search -->
<div>
<label for="search-input" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
</svg>
Suchen
</label>
<input type="text" id="search-input" placeholder="Drucker-Name, Modell oder IP..."
class="mercedes-form-input block w-full px-4 py-3 rounded-lg">
</div>
<!-- Status Filter -->
<div>
<label for="filterStatus" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.414A1 1 0 013 6.707V4z"/>
</svg>
Status
</label>
<select id="filterStatus" class="mercedes-form-input block w-full px-4 py-3 rounded-lg">
<option value="">Alle Status</option>
<option value="online">Online</option>
<option value="offline">Offline</option>
<option value="printing">Druckt</option>
<option value="error">Fehler</option>
<option value="maintenance">Wartung</option>
</select>
</div>
<!-- Location Filter -->
<div>
<label for="filterLocation" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
<svg class="w-4 h-4 inline mr-1" 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>
Standort
</label>
<select id="filterLocation" class="mercedes-form-input block w-full px-4 py-3 rounded-lg">
<option value="">Alle Standorte</option>
<!-- Wird dynamisch gefüllt -->
</select>
</div>
<!-- Model Filter -->
<div>
<label for="filterModel" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>
</svg>
Modell
</label>
<select id="filterModel" class="mercedes-form-input block w-full px-4 py-3 rounded-lg">
<option value="">Alle Modelle</option>
<!-- Wird dynamisch gefüllt -->
</select>
</div>
</div>
<!-- Advanced Filter Options -->
<div class="mt-6 flex flex-wrap items-center justify-between gap-4">
<div class="flex flex-wrap gap-4">
<div class="flex items-center">
<input type="checkbox" id="show-offline" class="w-4 h-4 text-mercedes-blue rounded">
<label for="show-offline" class="ml-2 text-sm text-mercedes-gray dark:text-slate-400">
Offline-Drucker anzeigen
</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="show-maintenance" class="w-4 h-4 text-mercedes-blue rounded">
<label for="show-maintenance" class="ml-2 text-sm text-mercedes-gray dark:text-slate-400">
Wartungsmodus anzeigen
</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="only-available" class="w-4 h-4 text-mercedes-blue rounded">
<label for="only-available" class="ml-2 text-sm text-mercedes-gray dark:text-slate-400">
Nur verfügbare Drucker
</label>
</div>
</div>
<div class="flex items-center gap-4">
<div class="text-sm text-mercedes-gray dark:text-slate-400">
<span id="filtered-count">0</span> von <span id="total-printers">0</span> Druckern
</div>
<div class="flex items-center gap-2">
<label for="sort-by" class="text-sm text-mercedes-gray dark:text-slate-400">Sortieren:</label>
<select id="sort-by" class="text-sm border border-mercedes-silver rounded px-2 py-1">
<option value="name">Name</option>
<option value="status">Status</option>
<option value="location">Standort</option>
<option value="model">Modell</option>
<option value="last_activity">Letzte Aktivität</option>
</select>
</div>
</div>
</div>
</div>
<!-- Enhanced Printers Grid -->
<div class="dashboard-card p-6">
<div class="flex items-center justify-between mb-6">
<div>
<h2 class="text-2xl font-bold text-mercedes-black dark:text-white mb-2">Drucker-Übersicht</h2>
<p class="text-mercedes-gray dark:text-slate-400">Live-Status aller registrierten 3D-Drucker</p>
</div>
<div class="flex items-center gap-3">
<div class="text-sm text-mercedes-gray dark:text-slate-400">
Ansicht:
</div>
<button onclick="toggleGridView('grid')" id="grid-view-btn"
class="p-2 rounded-lg transition-colors bg-mercedes-blue text-white">
<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="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"/>
</svg>
</button>
<button onclick="toggleGridView('list')" id="list-view-btn"
class="p-2 rounded-lg transition-colors text-mercedes-gray hover:bg-mercedes-silver">
<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="M4 6h16M4 10h16M4 14h16M4 18h16"/>
</svg>
</button>
</div>
</div>
<!-- Loading State -->
<div id="loading-state" class="space-y-6">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="printer-card p-6">
<div class="flex items-center gap-4 mb-4">
<div class="loading-skeleton w-12 h-12 rounded-xl"></div>
<div class="flex-1">
<div class="loading-skeleton h-5 w-3/4 mb-2"></div>
<div class="loading-skeleton h-4 w-1/2"></div>
</div>
<div class="loading-skeleton w-16 h-6 rounded-full"></div>
</div>
<div class="space-y-3 mb-4">
<div class="loading-skeleton h-4 w-full"></div>
<div class="loading-skeleton h-4 w-2/3"></div>
<div class="loading-skeleton h-4 w-1/2"></div>
</div>
<div class="flex gap-2">
<div class="loading-skeleton h-8 w-20 rounded-lg"></div>
<div class="loading-skeleton h-8 w-24 rounded-lg"></div>
</div>
</div>
<div class="printer-card p-6">
<div class="flex items-center gap-4 mb-4">
<div class="loading-skeleton w-12 h-12 rounded-xl"></div>
<div class="flex-1">
<div class="loading-skeleton h-5 w-3/4 mb-2"></div>
<div class="loading-skeleton h-4 w-1/2"></div>
</div>
<div class="loading-skeleton w-16 h-6 rounded-full"></div>
</div>
<div class="space-y-3 mb-4">
<div class="loading-skeleton h-4 w-full"></div>
<div class="loading-skeleton h-4 w-2/3"></div>
<div class="loading-skeleton h-4 w-1/2"></div>
</div>
<div class="flex gap-2">
<div class="loading-skeleton h-8 w-20 rounded-lg"></div>
<div class="loading-skeleton h-8 w-24 rounded-lg"></div>
</div>
</div>
<div class="printer-card p-6">
<div class="flex items-center gap-4 mb-4">
<div class="loading-skeleton w-12 h-12 rounded-xl"></div>
<div class="flex-1">
<div class="loading-skeleton h-5 w-3/4 mb-2"></div>
<div class="loading-skeleton h-4 w-1/2"></div>
</div>
<div class="loading-skeleton w-16 h-6 rounded-full"></div>
</div>
<div class="space-y-3 mb-4">
<div class="loading-skeleton h-4 w-full"></div>
<div class="loading-skeleton h-4 w-2/3"></div>
<div class="loading-skeleton h-4 w-1/2"></div>
</div>
<div class="flex gap-2">
<div class="loading-skeleton h-8 w-20 rounded-lg"></div>
<div class="loading-skeleton h-8 w-24 rounded-lg"></div>
</div>
</div>
</div>
<div class="text-center">
<div class="text-mercedes-gray dark:text-slate-400 mb-4">
<svg class="w-8 h-8 mx-auto mb-3 animate-spin text-mercedes-blue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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>
</div>
<p class="text-mercedes-gray dark:text-slate-400">Lade Drucker-Informationen...</p>
</div>
</div>
<!-- Printers Grid Container -->
<div id="printers-container" class="hidden">
<div id="printers-grid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Printers will be loaded here -->
</div>
<!-- Empty State -->
<div id="empty-state" class="hidden col-span-full py-16 text-center">
<div class="max-w-md mx-auto">
<div class="text-mercedes-silver mb-6">
<svg class="w-20 h-20 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"/>
</svg>
</div>
<h3 class="text-2xl font-semibold text-mercedes-black dark:text-white mb-3">Keine Drucker gefunden</h3>
<p class="text-mercedes-gray dark:text-slate-400 mb-8 leading-relaxed">
Es wurden keine 3D-Drucker gefunden, die den aktuellen Filterkriterien entsprechen.
Versuchen Sie, die Filter zu ändern oder fügen Sie Ihren ersten Drucker hinzu.
</p>
<div class="flex flex-col sm:flex-row gap-3 justify-center">
<button onclick="clearAllFilters()" class="btn-secondary">
Filter zurücksetzen
</button>
<button onclick="openAddPrinterModal()" class="btn-primary">
Ersten Drucker hinzufügen
</button>
</div>
</div>
</div>
<!-- No Results State -->
<div id="no-results-state" class="hidden col-span-full py-16 text-center">
<div class="max-w-md mx-auto">
<div class="text-mercedes-silver mb-6">
<svg class="w-16 h-16 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
</svg>
</div>
<h3 class="text-xl font-semibold text-mercedes-black dark:text-white mb-3">Keine passenden Drucker</h3>
<p class="text-mercedes-gray dark:text-slate-400 mb-6">
Ihre Suche ergab keine Treffer. Versuchen Sie andere Suchbegriffe oder erweitern Sie Ihre Filter.
</p>
<button onclick="clearAllFilters()" class="btn-secondary">
Alle Filter zurücksetzen
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Enhanced Add/Edit Printer Modal -->
<div id="printerModal" 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="mercedes-modal max-w-2xl w-full p-8 transform transition-all duration-300 scale-95 opacity-0" id="printerModalContent">
<div class="flex justify-between items-center mb-6">
<div>
<h3 id="printerModalTitle" class="text-2xl font-bold text-mercedes-black dark:text-white mb-2">
Drucker hinzufügen
</h3>
<p class="text-mercedes-gray dark:text-slate-400">Konfigurieren Sie einen neuen 3D-Drucker für das System</p>
</div>
<button onclick="closePrinterModal()" class="p-2 hover:bg-gray-100 dark:hover:bg-slate-700 rounded-lg transition-colors">
<svg class="w-6 h-6 text-mercedes-gray dark:text-slate-400" 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="printerForm" class="space-y-6">
<input type="hidden" id="printerId" name="printerId">
<!-- Basic Information -->
<div class="space-y-4">
<h4 class="text-lg font-semibold text-mercedes-black dark:text-white border-b border-mercedes-silver pb-2">
Grundinformationen
</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="printerName" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
Drucker-Name <span class="text-red-500">*</span>
</label>
<input type="text" id="printerName" name="name" required
class="mercedes-form-input block w-full px-4 py-3 rounded-lg"
placeholder="z.B. Ultimaker S3 #01">
<p class="text-xs text-mercedes-gray dark:text-slate-400 mt-1">Eindeutiger Name zur Identifikation</p>
</div>
<div>
<label for="printerModel" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
Modell
</label>
<select id="printerModel" name="model" class="mercedes-form-input block w-full px-4 py-3 rounded-lg">
<option value="">Modell auswählen...</option>
<option value="Ultimaker S3">Ultimaker S3</option>
<option value="Ultimaker S5">Ultimaker S5</option>
<option value="Prusa MK3S+">Prusa MK3S+</option>
<option value="Bambu Lab X1 Carbon">Bambu Lab X1 Carbon</option>
<option value="Formlabs Form 3">Formlabs Form 3</option>
<option value="Custom">Benutzerdefiniert</option>
</select>
</div>
<div id="customModelDiv" class="hidden md:col-span-2">
<label for="customModel" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
Benutzerdefiniertes Modell
</label>
<input type="text" id="customModel" name="customModel"
class="mercedes-form-input block w-full px-4 py-3 rounded-lg"
placeholder="Geben Sie das Druckermodell ein">
</div>
<div>
<label for="printerLocation" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
Standort <span class="text-red-500">*</span>
</label>
<input type="text" id="printerLocation" name="location" required
class="mercedes-form-input block w-full px-4 py-3 rounded-lg"
placeholder="z.B. Produktionshalle A, Raum 101">
</div>
<div>
<label for="printerIP" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
IP-Adresse
</label>
<input type="text" id="printerIP" name="ip_address"
class="mercedes-form-input block w-full px-4 py-3 rounded-lg"
placeholder="192.168.1.100"
pattern="^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$">
<p class="text-xs text-mercedes-gray dark:text-slate-400 mt-1">Für Netzwerk-Überwachung</p>
</div>
</div>
</div>
<!-- Technical Specifications -->
<div class="space-y-4">
<h4 class="text-lg font-semibold text-mercedes-black dark:text-white border-b border-mercedes-silver pb-2">
Technische Spezifikationen
</h4>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label for="buildVolumeX" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
Bauvolumen X (mm)
</label>
<input type="number" id="buildVolumeX" name="build_volume_x" min="0" max="1000"
class="mercedes-form-input block w-full px-4 py-3 rounded-lg"
placeholder="330">
</div>
<div>
<label for="buildVolumeY" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
Bauvolumen Y (mm)
</label>
<input type="number" id="buildVolumeY" name="build_volume_y" min="0" max="1000"
class="mercedes-form-input block w-full px-4 py-3 rounded-lg"
placeholder="240">
</div>
<div>
<label for="buildVolumeZ" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
Bauvolumen Z (mm)
</label>
<input type="number" id="buildVolumeZ" name="build_volume_z" min="0" max="1000"
class="mercedes-form-input block w-full px-4 py-3 rounded-lg"
placeholder="300">
</div>
<div>
<label for="nozzleTemp" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
Max. Düsentemperatur (°C)
</label>
<input type="number" id="nozzleTemp" name="max_nozzle_temp" min="0" max="500"
class="mercedes-form-input block w-full px-4 py-3 rounded-lg"
placeholder="280">
</div>
<div>
<label for="bedTemp" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
Max. Betttemperatur (°C)
</label>
<input type="number" id="bedTemp" name="max_bed_temp" min="0" max="200"
class="mercedes-form-input block w-full px-4 py-3 rounded-lg"
placeholder="100">
</div>
<div>
<label for="filamentTypes" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
Unterstützte Materialien
</label>
<select id="filamentTypes" name="supported_filaments" multiple
class="mercedes-form-input block w-full px-4 py-3 rounded-lg">
<option value="PLA">PLA</option>
<option value="ABS">ABS</option>
<option value="PETG">PETG</option>
<option value="TPU">TPU</option>
<option value="ASA">ASA</option>
<option value="PC">PC (Polycarbonat)</option>
<option value="Nylon">Nylon</option>
</select>
<p class="text-xs text-mercedes-gray dark:text-slate-400 mt-1">Mehrfachauswahl möglich</p>
</div>
</div>
</div>
<!-- Settings -->
<div class="space-y-4">
<h4 class="text-lg font-semibold text-mercedes-black dark:text-white border-b border-mercedes-silver pb-2">
Einstellungen
</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-4">
<div class="flex items-center">
<input type="checkbox" id="printerActive" name="active" checked
class="w-4 h-4 text-mercedes-blue bg-white dark:bg-slate-800 border-mercedes-silver rounded focus:ring-mercedes-blue focus:ring-2">
<label for="printerActive" class="ml-3 text-sm text-mercedes-black dark:text-slate-300">
Drucker aktiv
</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="autoQueue" name="auto_queue"
class="w-4 h-4 text-mercedes-blue bg-white dark:bg-slate-800 border-mercedes-silver rounded focus:ring-mercedes-blue focus:ring-2">
<label for="autoQueue" class="ml-3 text-sm text-mercedes-black dark:text-slate-300">
Automatische Warteschlange
</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="enableMonitoring" name="enable_monitoring"
class="w-4 h-4 text-mercedes-blue bg-white dark:bg-slate-800 border-mercedes-silver rounded focus:ring-mercedes-blue focus:ring-2">
<label for="enableMonitoring" class="ml-3 text-sm text-mercedes-black dark:text-slate-300">
Live-Überwachung aktivieren
</label>
</div>
</div>
<div>
<label for="printerNotes" class="block text-sm font-medium text-mercedes-black dark:text-slate-300 mb-2">
Notizen
</label>
<textarea id="printerNotes" name="notes" rows="4"
class="mercedes-form-input block w-full px-4 py-3 rounded-lg resize-none"
placeholder="Zusätzliche Informationen zum Drucker..."></textarea>
</div>
</div>
</div>
<!-- Connection Test -->
<div id="connectionTest" class="hidden space-y-4">
<h4 class="text-lg font-semibold text-mercedes-black dark:text-white border-b border-mercedes-silver pb-2">
Verbindungstest
</h4>
<div class="p-4 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-blue-800 dark:text-blue-400">Verbindung testen</p>
<p class="text-xs text-blue-600 dark:text-blue-300">Prüfen Sie die Netzwerkverbindung zum Drucker</p>
</div>
<button type="button" onclick="testPrinterConnection()" id="testConnectionBtn"
class="px-4 py-2 bg-blue-600 text-white text-sm rounded-lg hover:bg-blue-700 transition-colors">
Test starten
</button>
</div>
<div id="connectionResult" class="mt-3 hidden"></div>
</div>
</div>
<div class="flex items-center justify-between pt-6 border-t border-mercedes-silver">
<button type="button" id="deletePrinterBtn" onclick="deletePrinter()" style="display: none;"
class="px-6 py-3 bg-red-600 hover:bg-red-700 text-white rounded-lg transition-colors flex items-center gap-2">
<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>
<div class="flex items-center gap-3">
<button type="button" onclick="closePrinterModal()" class="btn-secondary">
Abbrechen
</button>
<button type="submit" class="btn-primary flex items-center gap-2">
<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="M5 13l4 4L19 7"/>
</svg>
Speichern
</button>
</div>
</div>
</form>
</div>
</div>
</div>
<!-- Enhanced Printer Details Modal -->
<div id="printerDetailsModal" 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="mercedes-modal max-w-6xl w-full p-8 transform transition-all duration-300 scale-95 opacity-0" id="printerDetailsModalContent">
<div class="flex justify-between items-center mb-6">
<div>
<h3 id="printerDetailsTitle" class="text-2xl font-bold text-mercedes-black dark:text-white mb-2">
Drucker Details
</h3>
<p class="text-mercedes-gray dark:text-slate-400">Live-Informationen und Steuerungsoptionen</p>
</div>
<button onclick="closePrinterDetailsModal()" class="p-2 hover:bg-gray-100 dark:hover:bg-slate-700 rounded-lg transition-colors">
<svg class="w-6 h-6 text-mercedes-gray dark:text-slate-400" 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="printerDetailsContent" class="space-y-6">
<!-- Content will be loaded dynamically -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Printer Status -->
<div class="lg:col-span-2 space-y-6">
<div class="monitoring-widget p-6">
<h4 class="text-lg font-semibold text-mercedes-black dark:text-white mb-4">Live Status</h4>
<div id="printer-status-info">
<!-- Dynamic content -->
</div>
</div>
<!-- Current Job -->
<div class="monitoring-widget p-6">
<h4 class="text-lg font-semibold text-mercedes-black dark:text-white mb-4">Aktueller Job</h4>
<div id="current-job-info">
<!-- Dynamic content -->
</div>
</div>
<!-- Recent Activity -->
<div class="monitoring-widget p-6">
<h4 class="text-lg font-semibold text-mercedes-black dark:text-white mb-4">Letzte Aktivitäten</h4>
<div id="recent-activity-info">
<!-- Dynamic content -->
</div>
</div>
</div>
<!-- Controls & Monitoring -->
<div class="space-y-6">
<!-- Temperature Monitoring -->
<div class="monitoring-widget p-6">
<h4 class="text-lg font-semibold text-mercedes-black dark:text-white mb-4">Temperaturen</h4>
<div id="temperature-info">
<!-- Dynamic content -->
</div>
</div>
<!-- Quick Actions -->
<div class="monitoring-widget p-6">
<h4 class="text-lg font-semibold text-mercedes-black dark:text-white mb-4">Schnellaktionen</h4>
<div id="quick-actions-info">
<!-- Dynamic content -->
</div>
</div>
<!-- Statistics -->
<div class="monitoring-widget p-6">
<h4 class="text-lg font-semibold text-mercedes-black dark:text-white mb-4">Statistiken</h4>
<div id="printer-statistics">
<!-- Dynamic content -->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let allPrinters = [];
let filteredPrinters = [];
document.addEventListener('DOMContentLoaded', function() {
loadPrinters();
setupFilters();
});
// Load printers from API
async function loadPrinters() {
try {
const response = await fetch('/api/printers');
const data = await response.json();
if (data.success) {
allPrinters = data.printers;
filteredPrinters = [...allPrinters];
displayPrinters();
updateStatistics();
populateLocationFilter();
} else {
showError('Fehler beim Laden der Drucker: ' + data.error);
}
} catch (error) {
console.error('Error loading printers:', error);
showError('Fehler beim Laden der Drucker');
} finally {
document.getElementById('loading-state').style.display = 'none';
}
}
// Display printers in grid
function displayPrinters() {
const grid = document.getElementById('printers-grid');
const emptyState = document.getElementById('empty-state');
if (filteredPrinters.length === 0) {
grid.style.display = 'none';
emptyState.classList.remove('hidden');
return;
}
grid.style.display = 'grid';
emptyState.classList.add('hidden');
grid.innerHTML = filteredPrinters.map(printer => createPrinterCard(printer)).join('');
}
// Create printer card HTML
function createPrinterCard(printer) {
const statusClass = getStatusClass(printer.status);
const statusIcon = getStatusIcon(printer.status);
return `
<div class="dashboard-card p-6 border-l-4 ${statusClass.border}">
<div class="flex justify-between items-start mb-4">
<div class="flex items-center gap-3">
<div class="w-10 h-10 ${statusClass.bg} rounded-lg flex items-center justify-center">
${statusIcon}
</div>
<div>
<h3 class="text-lg font-semibold text-slate-900 dark:text-white">${printer.name}</h3>
<p class="text-sm text-slate-500 dark:text-slate-400">${printer.model || 'Unbekanntes Modell'}</p>
</div>
</div>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium ${statusClass.badge}">
${printer.status_text || printer.status}
</span>
</div>
<div class="space-y-3 mb-4">
<div class="flex justify-between text-sm">
<span class="text-slate-500 dark:text-slate-400">Standort:</span>
<span class="text-slate-900 dark:text-white">${printer.location || 'Nicht angegeben'}</span>
</div>
<div class="flex justify-between text-sm">
<span class="text-slate-500 dark:text-slate-400">IP-Adresse:</span>
<span class="text-slate-900 dark:text-white">${printer.ip_address || 'Nicht konfiguriert'}</span>
</div>
<div class="flex justify-between text-sm">
<span class="text-slate-500 dark:text-slate-400">Letzter Job:</span>
<span class="text-slate-900 dark:text-white">${printer.last_job || 'Kein Job'}</span>
</div>
</div>
<div class="flex gap-2">
<button onclick="showPrinterDetails(${printer.id})"
class="flex-1 px-3 py-2 text-sm bg-gray-100 dark:bg-slate-700 text-slate-700 dark:text-slate-300 rounded-lg hover:bg-gray-200 dark:hover:bg-slate-600 transition-colors">
Details
</button>
<button onclick="editPrinter(${printer.id})"
class="flex-1 px-3 py-2 text-sm bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400 rounded-lg hover:bg-blue-200 dark:hover:bg-blue-900/50 transition-colors">
Bearbeiten
</button>
</div>
</div>
`;
}
// Get status styling classes
function getStatusClass(status) {
const classes = {
'online': {
border: 'border-green-400',
bg: 'bg-green-100 dark:bg-green-900/30',
badge: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'
},
'offline': {
border: 'border-red-400',
bg: 'bg-red-100 dark:bg-red-900/30',
badge: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400'
},
'printing': {
border: 'border-blue-400',
bg: 'bg-blue-100 dark:bg-blue-900/30',
badge: 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400'
},
'error': {
border: 'border-orange-400',
bg: 'bg-orange-100 dark:bg-orange-900/30',
badge: 'bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-400'
}
};
return classes[status] || classes['offline'];
}
// Get status icon
function getStatusIcon(status) {
const icons = {
'online': '<div class="w-3 h-3 bg-green-500 rounded-full"></div>',
'offline': '<div class="w-3 h-3 bg-red-500 rounded-full"></div>',
'printing': '<div class="w-3 h-3 bg-blue-500 rounded-full animate-pulse"></div>',
'error': '<div class="w-3 h-3 bg-orange-500 rounded-full"></div>'
};
return icons[status] || icons['offline'];
}
// Update statistics
function updateStatistics() {
const stats = {
total: filteredPrinters.length,
online: filteredPrinters.filter(p => p.status === 'online').length,
offline: filteredPrinters.filter(p => p.status === 'offline').length,
printing: filteredPrinters.filter(p => p.status === 'printing').length
};
document.getElementById('total-count').textContent = stats.total;
document.getElementById('online-count').textContent = stats.online;
document.getElementById('offline-count').textContent = stats.offline;
document.getElementById('printing-count').textContent = stats.printing;
}
// Setup filter functionality
function setupFilters() {
const statusFilter = document.getElementById('filterStatus');
const locationFilter = document.getElementById('filterLocation');
statusFilter.addEventListener('change', applyFilters);
locationFilter.addEventListener('change', applyFilters);
}
// Apply filters
function applyFilters() {
const statusFilter = document.getElementById('filterStatus').value;
const locationFilter = document.getElementById('filterLocation').value;
filteredPrinters = allPrinters.filter(printer => {
const statusMatch = !statusFilter || printer.status === statusFilter;
const locationMatch = !locationFilter || printer.location === locationFilter;
return statusMatch && locationMatch;
});
displayPrinters();
updateStatistics();
}
// Populate location filter
function populateLocationFilter() {
const locationFilter = document.getElementById('filterLocation');
const locations = [...new Set(allPrinters.map(p => p.location).filter(Boolean))];
locationFilter.innerHTML = '<option value="">Alle Standorte</option>' +
locations.map(location => `<option value="${location}">${location}</option>`).join('');
}
// Modal functions
function openAddPrinterModal() {
const modal = document.getElementById('printerModal');
const modalContent = document.getElementById('printerModalContent');
document.getElementById('printerModalTitle').textContent = 'Drucker hinzufügen';
document.getElementById('printerForm').reset();
document.getElementById('printerId').value = '';
document.getElementById('deletePrinterBtn').style.display = 'none';
modal.classList.remove('hidden');
setTimeout(() => {
modalContent.classList.remove('scale-95', 'opacity-0');
modalContent.classList.add('scale-100', 'opacity-100');
}, 10);
}
function closePrinterModal() {
const modal = document.getElementById('printerModal');
const modalContent = document.getElementById('printerModalContent');
modalContent.classList.remove('scale-100', 'opacity-100');
modalContent.classList.add('scale-95', 'opacity-0');
setTimeout(() => {
modal.classList.add('hidden');
}, 200);
}
function editPrinter(printerId) {
const printer = allPrinters.find(p => p.id === printerId);
if (!printer) return;
document.getElementById('printerModalTitle').textContent = 'Drucker bearbeiten';
document.getElementById('printerId').value = printer.id;
document.getElementById('printerName').value = printer.name;
document.getElementById('printerModel').value = printer.model || '';
document.getElementById('printerLocation').value = printer.location || '';
document.getElementById('printerIP').value = printer.ip_address || '';
document.getElementById('printerActive').checked = printer.active;
document.getElementById('deletePrinterBtn').style.display = 'block';
openAddPrinterModal();
}
function showPrinterDetails(printerId) {
// Implementation for showing printer details
console.log('Show details for printer:', printerId);
}
function closePrinterDetailsModal() {
const modal = document.getElementById('printerDetailsModal');
const modalContent = document.getElementById('printerDetailsModalContent');
modalContent.classList.remove('scale-100', 'opacity-100');
modalContent.classList.add('scale-95', 'opacity-0');
setTimeout(() => {
modal.classList.add('hidden');
}, 200);
}
function deletePrinter() {
const printerId = document.getElementById('printerId').value;
if (printerId && confirm('Möchten Sie diesen Drucker wirklich löschen?')) {
// Implementation for deleting printer
console.log('Delete printer:', printerId);
closePrinterModal();
}
}
function refreshPrinters() {
document.getElementById('loading-state').style.display = 'block';
document.getElementById('printers-grid').style.display = 'none';
document.getElementById('empty-state').classList.add('hidden');
loadPrinters();
}
function showError(message) {
console.error(message);
// You could implement a toast notification here
}
// Form submission
document.getElementById('printerForm').addEventListener('submit', async function(e) {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
data.active = document.getElementById('printerActive').checked;
try {
const url = data.printerId ? `/api/printers/${data.printerId}` : '/api/printers';
const method = data.printerId ? 'PUT' : 'POST';
const response = await fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
},
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
closePrinterModal();
refreshPrinters();
} else {
showError('Fehler beim Speichern: ' + result.error);
}
} catch (error) {
console.error('Error saving printer:', error);
showError('Fehler beim Speichern des Druckers');
}
});
</script>
{% endblock %}