"Update theme
This commit is contained in:
parent
998244db68
commit
f927048570
@ -713,4 +713,271 @@
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Professional Mini Button Styles */
|
||||
.btn-professional-mini {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
color: var(--mb-primary);
|
||||
border: 1px solid rgba(59, 130, 246, 0.2);
|
||||
border-radius: 0.75rem;
|
||||
padding: 0.5rem 1rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dark .btn-professional-mini {
|
||||
background: rgba(59, 130, 246, 0.2);
|
||||
border-color: rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.btn-professional-mini:hover {
|
||||
background: rgba(59, 130, 246, 0.2);
|
||||
border-color: var(--mb-primary);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.dark .btn-professional-mini:hover {
|
||||
background: rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
/* Danger Mini Button */
|
||||
.btn-danger-professional-mini {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
color: #dc2626;
|
||||
border: 1px solid rgba(239, 68, 68, 0.2);
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dark .btn-danger-professional-mini {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
color: #ef4444;
|
||||
border-color: rgba(239, 68, 68, 0.3);
|
||||
}
|
||||
|
||||
.btn-danger-professional-mini:hover {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
border-color: #dc2626;
|
||||
transform: translateY(-1px) scale(1.05);
|
||||
}
|
||||
|
||||
.dark .btn-danger-professional-mini:hover {
|
||||
background: rgba(239, 68, 68, 0.3);
|
||||
border-color: #ef4444;
|
||||
}
|
||||
|
||||
/* Success Button */
|
||||
.btn-success-professional {
|
||||
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 1rem;
|
||||
padding: 0.75rem 2rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
letter-spacing: 0.025em;
|
||||
text-transform: uppercase;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-success-professional::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: left 0.5s;
|
||||
}
|
||||
|
||||
.btn-success-professional:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.btn-success-professional:hover {
|
||||
background: linear-gradient(135deg, #059669 0%, #047857 100%);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(16, 185, 129, 0.4);
|
||||
}
|
||||
|
||||
.btn-success-professional:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Danger Button */
|
||||
.btn-danger-professional {
|
||||
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 1rem;
|
||||
padding: 0.75rem 2rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
letter-spacing: 0.025em;
|
||||
text-transform: uppercase;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 15px rgba(239, 68, 68, 0.3);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-danger-professional::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: left 0.5s;
|
||||
}
|
||||
|
||||
.btn-danger-professional:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.btn-danger-professional:hover {
|
||||
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(239, 68, 68, 0.4);
|
||||
}
|
||||
|
||||
.btn-danger-professional:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Filter Button Mercedes */
|
||||
.filter-btn-mercedes {
|
||||
background: transparent;
|
||||
color: var(--light-text-muted);
|
||||
border: none;
|
||||
border-radius: 0.75rem;
|
||||
padding: 0.5rem 1rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dark .filter-btn-mercedes {
|
||||
color: var(--dark-text-muted);
|
||||
}
|
||||
|
||||
.filter-btn-mercedes:hover {
|
||||
color: var(--light-text-primary);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.dark .filter-btn-mercedes:hover {
|
||||
color: var(--dark-text-primary);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.filter-btn-mercedes.active {
|
||||
background: var(--mb-primary);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
/* Status Dots */
|
||||
.status-dot {
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.status-dot.status-online {
|
||||
background: #10b981;
|
||||
box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
|
||||
}
|
||||
|
||||
.status-dot.status-offline {
|
||||
background: #ef4444;
|
||||
box-shadow: 0 0 10px rgba(239, 68, 68, 0.5);
|
||||
}
|
||||
|
||||
.status-dot.status-online::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background: inherit;
|
||||
animation: pulse-professional 2s infinite;
|
||||
}
|
||||
|
||||
/* Professional Shadow */
|
||||
.professional-shadow {
|
||||
box-shadow: 0 8px 32px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
.dark .professional-shadow {
|
||||
box-shadow: 0 8px 32px rgba(59, 130, 246, 0.4);
|
||||
}
|
||||
|
||||
/* Professional Accent Colors */
|
||||
.text-professional-accent {
|
||||
color: var(--mb-accent);
|
||||
}
|
||||
|
||||
.bg-professional-accent {
|
||||
background-color: var(--mb-accent);
|
||||
}
|
||||
|
||||
/* Animate Spin Slow */
|
||||
.animate-spin-slow {
|
||||
animation: spin 3s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse-professional {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
transform: translate(-50%, -50%) scale(1.2);
|
||||
}
|
||||
}
|
@ -724,7 +724,7 @@ function refreshPrinters() {
|
||||
if (refreshBtn) {
|
||||
refreshBtn.disabled = true;
|
||||
refreshBtn.innerHTML = `
|
||||
<svg class="animate-spin h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<svg class="animate-spin w-6 h-6 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" class="opacity-25"></circle>
|
||||
<path fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" class="opacity-75"></path>
|
||||
</svg>
|
||||
@ -734,10 +734,12 @@ function refreshPrinters() {
|
||||
|
||||
// Loading-State im Grid anzeigen
|
||||
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">Überprüfe Drucker-Status...</p>
|
||||
<p class="mt-1 text-xs text-slate-500 dark:text-slate-500">Dies kann bis zu 7 Sekunden dauern</p>
|
||||
<div class="col-span-full text-center py-12">
|
||||
<div class="mb-glass rounded-3xl p-12 max-w-md mx-auto">
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-professional-accent mx-auto mb-6"></div>
|
||||
<h3 class="text-xl font-bold text-professional-primary mb-3">Überprüfe Drucker-Status...</h3>
|
||||
<p class="text-professional-muted">Dies kann bis zu 7 Sekunden dauern</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@ -747,49 +749,51 @@ function refreshPrinters() {
|
||||
if (refreshBtn) {
|
||||
refreshBtn.disabled = false;
|
||||
refreshBtn.innerHTML = `
|
||||
<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 class="w-6 h-6 mr-3 group-hover:rotate-180 transition-transform duration-500" 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>
|
||||
Jetzt aktualisieren
|
||||
<span>Jetzt aktualisieren</span>
|
||||
`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Show error message
|
||||
// Show error message with Professional Styling
|
||||
function showError(message) {
|
||||
const grid = document.getElementById('printers-grid');
|
||||
if (!grid) return;
|
||||
|
||||
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>
|
||||
<h3 class="text-lg font-medium text-slate-900 dark:text-white mb-2">Drucker konnten nicht geladen werden</h3>
|
||||
<p class="text-slate-700 dark:text-slate-300 text-sm sm:text-base mb-4 max-w-md mx-auto">${message}</p>
|
||||
<div class="flex flex-col sm:flex-row gap-3 justify-center items-center">
|
||||
<button onclick="loadPrinters()" class="bg-indigo-600 hover:bg-indigo-700 dark:bg-indigo-600 dark:hover:bg-indigo-500 text-white px-4 sm:px-6 py-2 rounded-lg transition-all duration-200 text-sm sm:text-base flex items-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="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>
|
||||
Erneut versuchen
|
||||
</button>
|
||||
<button onclick="refreshPrinters()" class="bg-green-600 hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-500 text-white px-4 sm:px-6 py-2 rounded-lg transition-all duration-200 text-sm sm:text-base flex items-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="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
Mit Status-Check
|
||||
</button>
|
||||
<div class="col-span-full text-center py-12">
|
||||
<div class="mb-glass rounded-3xl p-12 max-w-lg mx-auto">
|
||||
<svg class="h-16 w-16 text-red-500 dark:text-red-400 mx-auto mb-6" 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>
|
||||
<h3 class="text-xl font-bold text-professional-primary mb-3">Drucker konnten nicht geladen werden</h3>
|
||||
<p class="text-professional-muted mb-6">${message}</p>
|
||||
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<button onclick="loadPrinters()" class="btn-professional group">
|
||||
<svg class="w-5 h-5 mr-3 group-hover:rotate-180 transition-transform duration-500" 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>
|
||||
<span>Erneut versuchen</span>
|
||||
</button>
|
||||
<button onclick="refreshPrinters()" class="btn-success-professional group">
|
||||
<svg class="w-5 h-5 mr-3 group-hover:scale-110 transition-transform duration-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<span>Mit Status-Check</span>
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-xs text-professional-muted mt-4">
|
||||
Tipp: "Mit Status-Check" dauert länger, überprüft aber die Verfügbarkeit aller Drucker
|
||||
</p>
|
||||
</div>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400 mt-3">
|
||||
Tipp: "Mit Status-Check" dauert länger, überprüft aber die Verfügbarkeit aller Drucker
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Show status message (success, info, warning, error)
|
||||
// Show status message (success, info, warning, error) - Professional Style
|
||||
function showStatusMessage(message, type = 'info') {
|
||||
// Vorzeitig beenden, wenn der Ladevorgang noch nicht abgeschlossen ist
|
||||
if (document.readyState !== 'complete') {
|
||||
@ -804,52 +808,62 @@ function showStatusMessage(message, type = 'info') {
|
||||
}
|
||||
|
||||
// Bestimme Farben basierend auf Typ
|
||||
let bgColor, textColor, iconPath;
|
||||
let bgColor, textColor, iconPath, borderColor;
|
||||
switch (type) {
|
||||
case 'success':
|
||||
bgColor = 'bg-green-100 dark:bg-green-900/30';
|
||||
bgColor = 'bg-green-50 dark:bg-green-900/20';
|
||||
textColor = 'text-green-800 dark:text-green-200';
|
||||
borderColor = 'border-green-200 dark:border-green-800/30';
|
||||
iconPath = 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z';
|
||||
break;
|
||||
case 'warning':
|
||||
bgColor = 'bg-yellow-100 dark:bg-yellow-900/30';
|
||||
bgColor = 'bg-yellow-50 dark:bg-yellow-900/20';
|
||||
textColor = 'text-yellow-800 dark:text-yellow-200';
|
||||
borderColor = 'border-yellow-200 dark:border-yellow-800/30';
|
||||
iconPath = 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z';
|
||||
break;
|
||||
case 'error':
|
||||
bgColor = 'bg-red-100 dark:bg-red-900/30';
|
||||
bgColor = 'bg-red-50 dark:bg-red-900/20';
|
||||
textColor = 'text-red-800 dark:text-red-200';
|
||||
borderColor = 'border-red-200 dark:border-red-800/30';
|
||||
iconPath = 'M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z';
|
||||
break;
|
||||
default: // info
|
||||
bgColor = 'bg-blue-100 dark:bg-blue-900/30';
|
||||
bgColor = 'bg-blue-50 dark:bg-blue-900/20';
|
||||
textColor = 'text-blue-800 dark:text-blue-200';
|
||||
borderColor = 'border-blue-200 dark:border-blue-800/30';
|
||||
iconPath = 'M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z';
|
||||
}
|
||||
|
||||
// Erstelle Status-Nachricht
|
||||
// Erstelle Status-Nachricht mit Professional Styling
|
||||
const statusMessage = document.createElement('div');
|
||||
statusMessage.id = 'status-message';
|
||||
statusMessage.className = `fixed top-4 right-4 z-50 ${bgColor} ${textColor} px-4 py-3 rounded-lg shadow-lg flex items-center space-x-3 max-w-md`;
|
||||
statusMessage.className = `fixed top-6 right-6 z-50 ${bgColor} ${textColor} ${borderColor} border backdrop-blur-sm rounded-2xl shadow-lg flex items-center space-x-4 px-6 py-4 max-w-md professional-shadow`;
|
||||
statusMessage.innerHTML = `
|
||||
<svg class="h-5 w-5 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${iconPath}" />
|
||||
<svg class="h-6 w-6 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="${iconPath}"/>
|
||||
</svg>
|
||||
<span class="text-sm font-medium">${message}</span>
|
||||
<button onclick="this.parentElement.remove()" class="ml-2 text-current hover:opacity-75">
|
||||
<svg class="h-4 w-4" 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"></path>
|
||||
<span class="text-sm font-medium flex-1">${message}</span>
|
||||
<button onclick="this.parentElement.remove()" class="ml-2 text-current hover:opacity-75 transition-opacity duration-200">
|
||||
<svg class="h-5 w-5" 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>
|
||||
`;
|
||||
|
||||
// Füge zur Seite hinzu
|
||||
// Füge zur Seite hinzu mit Animation
|
||||
document.body.appendChild(statusMessage);
|
||||
statusMessage.style.transform = 'translateX(100%)';
|
||||
setTimeout(() => {
|
||||
statusMessage.style.transform = 'translateX(0)';
|
||||
statusMessage.style.transition = 'transform 0.3s ease-out';
|
||||
}, 10);
|
||||
|
||||
// Automatisch nach 5 Sekunden entfernen
|
||||
setTimeout(() => {
|
||||
if (statusMessage && statusMessage.parentElement) {
|
||||
statusMessage.remove();
|
||||
statusMessage.style.transform = 'translateX(100%)';
|
||||
setTimeout(() => statusMessage.remove(), 300);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
@ -861,10 +875,12 @@ async function loadPrinters() {
|
||||
|
||||
// Loading-State anzeigen
|
||||
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>
|
||||
<p class="mt-1 text-xs text-slate-500 dark:text-slate-500">Dies sollte nur wenige Sekunden dauern</p>
|
||||
<div class="col-span-full text-center py-12">
|
||||
<div class="mb-glass rounded-3xl p-12 max-w-md mx-auto">
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-professional-accent mx-auto mb-6"></div>
|
||||
<h3 class="text-xl font-bold text-professional-primary mb-3">Lade Drucker...</h3>
|
||||
<p class="text-professional-muted">Dies sollte nur wenige Sekunden dauern</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@ -1018,62 +1034,53 @@ async function loadPrintersWithLiveStatus() {
|
||||
function toggleAutoRefresh() {
|
||||
autoRefreshEnabled = !autoRefreshEnabled;
|
||||
const btn = document.getElementById('auto-refresh-btn');
|
||||
const icon = document.getElementById('auto-refresh-icon');
|
||||
|
||||
if (autoRefreshEnabled) {
|
||||
if (btn) {
|
||||
btn.classList.remove('bg-blue-600', 'hover:bg-blue-700');
|
||||
btn.classList.add('bg-green-600', 'hover:bg-green-700');
|
||||
btn.classList.remove('btn-professional');
|
||||
btn.classList.add('btn-success-professional');
|
||||
btn.innerHTML = `
|
||||
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2 animate-spin" 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 class="w-6 h-6 mr-3 animate-spin" 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>
|
||||
Auto-Update AN
|
||||
<span>Auto-Update AN</span>
|
||||
`;
|
||||
}
|
||||
|
||||
// Starte Auto-Refresh
|
||||
startAutoRefresh();
|
||||
showStatusMessage('Auto-Update aktiviert - Drucker werden alle 30 Sekunden aktualisiert', 'info');
|
||||
showStatusMessage('Auto-Update aktiviert - Drucker werden alle 30 Sekunden aktualisiert', 'success');
|
||||
} else {
|
||||
if (btn) {
|
||||
btn.classList.remove('bg-green-600', 'hover:bg-green-700');
|
||||
btn.classList.add('bg-blue-600', 'hover:bg-blue-700');
|
||||
btn.classList.remove('btn-success-professional');
|
||||
btn.classList.add('btn-professional');
|
||||
btn.innerHTML = `
|
||||
<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 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<svg class="w-6 h-6 mr-3 group-hover:rotate-180 transition-transform duration-500" 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>
|
||||
Auto-Update
|
||||
<span>Auto-Update</span>
|
||||
`;
|
||||
}
|
||||
|
||||
// Stoppe Auto-Refresh
|
||||
stopAutoRefresh();
|
||||
showStatusMessage('Auto-Update deaktiviert', 'info');
|
||||
}
|
||||
}
|
||||
|
||||
function startAutoRefresh() {
|
||||
stopAutoRefresh(); // Stoppe vorherige Intervalle
|
||||
stopAutoRefresh(); // Clear any existing interval
|
||||
|
||||
autoRefreshInterval = setInterval(() => {
|
||||
loadPrintersWithLiveStatus();
|
||||
}, 30000); // 30 seconds
|
||||
|
||||
// Start countdown timer
|
||||
nextUpdateTime = 30;
|
||||
updateNextUpdateDisplay();
|
||||
|
||||
// Countdown-Timer
|
||||
nextUpdateCountdown = setInterval(() => {
|
||||
nextUpdateTime--;
|
||||
updateNextUpdateDisplay();
|
||||
|
||||
if (nextUpdateTime <= 0) {
|
||||
loadPrintersWithLiveStatus();
|
||||
nextUpdateTime = 30;
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Auto-Refresh-Interval
|
||||
autoRefreshInterval = setInterval(() => {
|
||||
loadPrintersWithLiveStatus();
|
||||
}, 30000); // Alle 30 Sekunden
|
||||
}
|
||||
|
||||
function stopAutoRefresh() {
|
||||
@ -1081,25 +1088,23 @@ function stopAutoRefresh() {
|
||||
clearInterval(autoRefreshInterval);
|
||||
autoRefreshInterval = null;
|
||||
}
|
||||
|
||||
if (nextUpdateCountdown) {
|
||||
clearInterval(nextUpdateCountdown);
|
||||
nextUpdateCountdown = null;
|
||||
}
|
||||
|
||||
const element = document.getElementById('next-update-time');
|
||||
if (element) element.textContent = '-';
|
||||
// Reset display
|
||||
const timeElement = document.getElementById('next-update-time');
|
||||
if (timeElement) {
|
||||
timeElement.textContent = '-';
|
||||
}
|
||||
}
|
||||
|
||||
function updateNextUpdateDisplay() {
|
||||
const element = document.getElementById('next-update-time');
|
||||
if (!element) return;
|
||||
|
||||
if (autoRefreshEnabled && nextUpdateTime > 0) {
|
||||
element.textContent = nextUpdateTime;
|
||||
element.parentElement.style.opacity = '1';
|
||||
} else {
|
||||
element.textContent = '-';
|
||||
element.parentElement.style.opacity = '0.5';
|
||||
const timeElement = document.getElementById('next-update-time');
|
||||
if (timeElement && autoRefreshEnabled) {
|
||||
timeElement.textContent = nextUpdateTime;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1113,149 +1118,105 @@ async function deletePrinter(printerId) {
|
||||
const response = await fetch(`/api/printers/${printerId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || 'Fehler beim Löschen des Druckers');
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
const result = await response.json();
|
||||
hidePrinterDetailModal();
|
||||
showStatusMessage(result.message || 'Drucker erfolgreich gelöscht', 'success');
|
||||
loadPrinters();
|
||||
if (response.ok && data.success) {
|
||||
showStatusMessage('Drucker erfolgreich gelöscht', 'success');
|
||||
// Drucker aus lokaler Liste entfernen
|
||||
printers = printers.filter(p => p.id !== printerId);
|
||||
renderPrinters();
|
||||
// Status-Übersicht aktualisieren
|
||||
const onlineCount = printers.filter(p => p.status === 'available' || p.is_online).length;
|
||||
const offlineCount = printers.length - onlineCount;
|
||||
updateStatusOverview(onlineCount, offlineCount, printers.length);
|
||||
} else {
|
||||
throw new Error(data.error || 'Fehler beim Löschen des Druckers');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting printer:', error);
|
||||
showStatusMessage('Fehler beim Löschen des Druckers: ' + error.message, 'error');
|
||||
showStatusMessage('Fehler beim Löschen: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Add printer
|
||||
// Handle add printer form submission
|
||||
async function handleAddPrinter(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const form = document.getElementById('addPrinterForm');
|
||||
const submitBtn = form.querySelector('button[type="submit"]');
|
||||
const form = event.target;
|
||||
const formData = new FormData(form);
|
||||
|
||||
// Deaktiviere Submit Button während der Verarbeitung
|
||||
const originalBtnText = submitBtn.innerHTML;
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = `
|
||||
<svg class="animate-spin h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" class="opacity-25"></circle>
|
||||
<path fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" class="opacity-75"></path>
|
||||
</svg>
|
||||
Wird hinzugefügt...
|
||||
`;
|
||||
|
||||
// Sammle und validiere Formulardaten
|
||||
const printerData = {
|
||||
name: formData.get('name')?.trim(),
|
||||
model: formData.get('model')?.trim(),
|
||||
location: formData.get('location')?.trim(),
|
||||
mac_address: formData.get('mac_address')?.trim().toUpperCase(),
|
||||
plug_ip: formData.get('plug_ip')?.trim(),
|
||||
plug_username: formData.get('plug_username')?.trim(),
|
||||
plug_password: formData.get('plug_password') // Passwort nicht trimmen
|
||||
};
|
||||
|
||||
// Client-seitige Validierung
|
||||
const errors = [];
|
||||
if (!printerData.name) errors.push('Name ist erforderlich');
|
||||
if (!printerData.model) errors.push('Modell ist erforderlich');
|
||||
if (!printerData.location) errors.push('Standort ist erforderlich');
|
||||
if (!printerData.mac_address) errors.push('MAC-Adresse ist erforderlich');
|
||||
if (!printerData.plug_ip) errors.push('Plug IP ist erforderlich');
|
||||
if (!printerData.plug_username) errors.push('Plug Benutzername ist erforderlich');
|
||||
if (!printerData.plug_password) errors.push('Plug Passwort ist erforderlich');
|
||||
|
||||
// MAC-Adresse Format validieren
|
||||
if (printerData.mac_address && !/^([0-9A-F]{2}:){5}[0-9A-F]{2}$/.test(printerData.mac_address)) {
|
||||
// Versuche automatische Formatierung
|
||||
const cleanMac = printerData.mac_address.replace(/[^0-9A-F]/g, '');
|
||||
if (cleanMac.length === 12) {
|
||||
printerData.mac_address = cleanMac.match(/.{2}/g).join(':');
|
||||
} else {
|
||||
errors.push('MAC-Adresse muss im Format AA:BB:CC:DD:EE:FF sein');
|
||||
// Validierung
|
||||
const requiredFields = ['name', 'model', 'location', 'mac_address', 'plug_ip', 'plug_username', 'plug_password'];
|
||||
for (let field of requiredFields) {
|
||||
if (!formData.get(field)) {
|
||||
showStatusMessage(`Bitte füllen Sie das Feld "${field}" aus`, 'warning');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// IP-Adresse Format validieren
|
||||
if (printerData.plug_ip && !/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(printerData.plug_ip)) {
|
||||
errors.push('IP-Adresse muss im Format 192.168.1.100 sein');
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
showStatusMessage(`Bitte korrigieren Sie folgende Fehler:\n${errors.join('\n')}`, 'error');
|
||||
// Button wieder aktivieren
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = originalBtnText;
|
||||
// MAC-Adresse Format validieren
|
||||
const macAddress = formData.get('mac_address');
|
||||
const macRegex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
|
||||
if (!macRegex.test(macAddress)) {
|
||||
showStatusMessage('Bitte geben Sie eine gültige MAC-Adresse ein (z.B. AA:BB:CC:DD:EE:FF)', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
// IP-Adresse Format validieren
|
||||
const ipAddress = formData.get('plug_ip');
|
||||
const ipRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
||||
if (!ipRegex.test(ipAddress)) {
|
||||
showStatusMessage('Bitte geben Sie eine gültige IP-Adresse ein (z.B. 192.168.1.100)', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
// Submit button deaktivieren
|
||||
const submitBtn = form.querySelector('button[type="submit"]');
|
||||
const originalBtnContent = submitBtn.innerHTML;
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = `
|
||||
<svg class="w-5 h-5 mr-3 animate-spin" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" class="opacity-25"></circle>
|
||||
<path fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" class="opacity-75"></path>
|
||||
</svg>
|
||||
<span>Wird hinzugefügt...</span>
|
||||
`;
|
||||
|
||||
try {
|
||||
// Erstelle AbortController für Timeout
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15 Sekunden Timeout
|
||||
|
||||
const response = await fetch('/api/printers/add', {
|
||||
const response = await fetch('/api/printers', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(printerData),
|
||||
signal: controller.signal
|
||||
body: formData
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
const data = await response.json();
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(result.error || `Server-Fehler: ${response.status}`);
|
||||
if (response.ok && data.success) {
|
||||
showStatusMessage('Drucker erfolgreich hinzugefügt', 'success');
|
||||
hideAddPrinterModal();
|
||||
// Drucker neu laden
|
||||
await loadPrinters();
|
||||
} else {
|
||||
throw new Error(data.error || 'Fehler beim Hinzufügen des Druckers');
|
||||
}
|
||||
|
||||
// Erfolg!
|
||||
hideAddPrinterModal();
|
||||
showStatusMessage(result.message || 'Drucker erfolgreich hinzugefügt', 'success');
|
||||
|
||||
// Drucker-Liste neu laden
|
||||
await loadPrinters();
|
||||
|
||||
console.log('Printer added successfully:', result.printer);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error adding printer:', error);
|
||||
|
||||
let errorMessage = 'Fehler beim Hinzufügen des Druckers';
|
||||
if (error.name === 'AbortError') {
|
||||
errorMessage = 'Timeout beim Hinzufügen des Druckers. Bitte versuchen Sie es erneut.';
|
||||
} else if (error.message) {
|
||||
errorMessage = error.message;
|
||||
}
|
||||
|
||||
showStatusMessage(errorMessage, 'error');
|
||||
showStatusMessage('Fehler beim Hinzufügen: ' + error.message, 'error');
|
||||
} finally {
|
||||
// Button wieder aktivieren
|
||||
// Submit button wieder aktivieren
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = originalBtnText;
|
||||
submitBtn.innerHTML = originalBtnContent;
|
||||
}
|
||||
}
|
||||
|
||||
// Make all functions globally available for onclick handlers
|
||||
window.showPrinterDetail = showPrinterDetail;
|
||||
window.hidePrinterDetailModal = hidePrinterDetailModal;
|
||||
window.deletePrinter = deletePrinter;
|
||||
window.loadPrinters = loadPrinters;
|
||||
window.handleAddPrinter = handleAddPrinter;
|
||||
window.toggleAutoRefresh = toggleAutoRefresh;
|
||||
window.loadPrintersWithLiveStatus = loadPrintersWithLiveStatus;
|
||||
// Make functions globally available
|
||||
window.refreshPrinters = refreshPrinters;
|
||||
window.hideAddPrinterModal = hideAddPrinterModal;
|
||||
window.showAddPrinterModal = showAddPrinterModal;
|
||||
window.showPrinterDetail = showPrinterDetail;
|
||||
window.deletePrinter = deletePrinter;
|
||||
window.toggleAutoRefresh = toggleAutoRefresh;
|
||||
</script>
|
||||
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user