📝 feat(database): Remove unneeded database files and update templates & tests 📆
This commit is contained in:
parent
2177975513
commit
d00fc592cc
Binary file not shown.
Binary file not shown.
@ -372,7 +372,7 @@
|
||||
</div>
|
||||
|
||||
<div class="text-sm">
|
||||
<a href="{{ url_for('reset_password_request') if url_for and url_for('reset_password_request') else '#' }}"
|
||||
<a href="#"
|
||||
class="text-mercedes-blue hover:text-blue-700 transition-colors">
|
||||
Passwort vergessen?
|
||||
</a>
|
||||
|
@ -1221,303 +1221,663 @@
|
||||
<script>
|
||||
let allPrinters = [];
|
||||
let filteredPrinters = [];
|
||||
let isAutoRefreshEnabled = true;
|
||||
let autoRefreshInterval;
|
||||
let currentGridView = 'grid';
|
||||
let isMaintenanceMode = false;
|
||||
|
||||
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';
|
||||
// Enhanced Printer Management System
|
||||
class PrinterManager {
|
||||
constructor() {
|
||||
this.init();
|
||||
this.setupEventListeners();
|
||||
this.startAutoRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
// Display printers in grid
|
||||
function displayPrinters() {
|
||||
const grid = document.getElementById('printers-grid');
|
||||
const emptyState = document.getElementById('empty-state');
|
||||
|
||||
async init() {
|
||||
await this.loadPrinters();
|
||||
this.setupFilters();
|
||||
this.initializePerformanceMonitoring();
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
// Filter Event Listeners
|
||||
document.getElementById('search-input').addEventListener('input', this.debounce(this.applyFilters.bind(this), 300));
|
||||
document.getElementById('filterStatus').addEventListener('change', this.applyFilters.bind(this));
|
||||
document.getElementById('filterLocation').addEventListener('change', this.applyFilters.bind(this));
|
||||
document.getElementById('filterModel').addEventListener('change', this.applyFilters.bind(this));
|
||||
document.getElementById('sort-by').addEventListener('change', this.sortAndDisplayPrinters.bind(this));
|
||||
|
||||
// Advanced Filter Checkboxes
|
||||
document.getElementById('show-offline').addEventListener('change', this.applyFilters.bind(this));
|
||||
document.getElementById('show-maintenance').addEventListener('change', this.applyFilters.bind(this));
|
||||
document.getElementById('only-available').addEventListener('change', this.applyFilters.bind(this));
|
||||
|
||||
// Modal Event Listeners
|
||||
document.getElementById('printerModel').addEventListener('change', this.handleModelChange.bind(this));
|
||||
document.getElementById('printerForm').addEventListener('submit', this.handleFormSubmit.bind(this));
|
||||
|
||||
// Keyboard Shortcuts
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
this.closeAllModals();
|
||||
}
|
||||
if (e.ctrlKey && e.key === 'r') {
|
||||
e.preventDefault();
|
||||
this.refreshPrinters();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async loadPrinters() {
|
||||
try {
|
||||
this.showLoadingState();
|
||||
|
||||
const response = await fetch('/api/printers');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
allPrinters = data.printers || [];
|
||||
this.applyFilters();
|
||||
this.updateStatistics();
|
||||
this.populateFilterDropdowns();
|
||||
this.updateLastUpdateTime();
|
||||
} else {
|
||||
this.showError('Fehler beim Laden der Drucker: ' + data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading printers:', error);
|
||||
this.showError('Fehler beim Laden der Drucker');
|
||||
} finally {
|
||||
this.hideLoadingState();
|
||||
}
|
||||
}
|
||||
|
||||
showLoadingState() {
|
||||
document.getElementById('loading-state').classList.remove('hidden');
|
||||
document.getElementById('printers-container').classList.add('hidden');
|
||||
}
|
||||
|
||||
hideLoadingState() {
|
||||
document.getElementById('loading-state').classList.add('hidden');
|
||||
document.getElementById('printers-container').classList.remove('hidden');
|
||||
}
|
||||
|
||||
applyFilters() {
|
||||
const searchTerm = document.getElementById('search-input').value.toLowerCase();
|
||||
const statusFilter = document.getElementById('filterStatus').value;
|
||||
const locationFilter = document.getElementById('filterLocation').value;
|
||||
const modelFilter = document.getElementById('filterModel').value;
|
||||
|
||||
const showOffline = document.getElementById('show-offline').checked;
|
||||
const showMaintenance = document.getElementById('show-maintenance').checked;
|
||||
const onlyAvailable = document.getElementById('only-available').checked;
|
||||
|
||||
filteredPrinters = allPrinters.filter(printer => {
|
||||
// Text search
|
||||
const matchesSearch = !searchTerm ||
|
||||
printer.name.toLowerCase().includes(searchTerm) ||
|
||||
(printer.model && printer.model.toLowerCase().includes(searchTerm)) ||
|
||||
(printer.ip_address && printer.ip_address.includes(searchTerm)) ||
|
||||
(printer.location && printer.location.toLowerCase().includes(searchTerm));
|
||||
|
||||
// Status filter
|
||||
const matchesStatus = !statusFilter || printer.status === statusFilter;
|
||||
const matchesLocation = !locationFilter || printer.location === locationFilter;
|
||||
const matchesModel = !modelFilter || printer.model === modelFilter;
|
||||
|
||||
// Advanced filters
|
||||
if (!showOffline && printer.status === 'offline') return false;
|
||||
if (!showMaintenance && printer.status === 'maintenance') return false;
|
||||
if (onlyAvailable && !this.isPrinterAvailable(printer)) return false;
|
||||
|
||||
return matchesSearch && matchesStatus && matchesLocation && matchesModel;
|
||||
});
|
||||
|
||||
this.updateFilterCounts();
|
||||
this.sortAndDisplayPrinters();
|
||||
}
|
||||
|
||||
isPrinterAvailable(printer) {
|
||||
return ['online', 'idle'].includes(printer.status);
|
||||
}
|
||||
|
||||
sortAndDisplayPrinters() {
|
||||
const sortBy = document.getElementById('sort-by').value;
|
||||
|
||||
filteredPrinters.sort((a, b) => {
|
||||
switch (sortBy) {
|
||||
case 'name':
|
||||
return a.name.localeCompare(b.name);
|
||||
case 'status':
|
||||
return a.status.localeCompare(b.status);
|
||||
case 'location':
|
||||
return (a.location || '').localeCompare(b.location || '');
|
||||
case 'model':
|
||||
return (a.model || '').localeCompare(b.model || '');
|
||||
case 'last_activity':
|
||||
return new Date(b.last_activity || 0) - new Date(a.last_activity || 0);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
this.displayPrinters();
|
||||
}
|
||||
|
||||
displayPrinters() {
|
||||
const grid = document.getElementById('printers-grid');
|
||||
const emptyState = document.getElementById('empty-state');
|
||||
const noResultsState = document.getElementById('no-results-state');
|
||||
|
||||
if (allPrinters.length === 0) {
|
||||
grid.innerHTML = '';
|
||||
emptyState.classList.remove('hidden');
|
||||
noResultsState.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
if (filteredPrinters.length === 0) {
|
||||
grid.style.display = 'none';
|
||||
emptyState.classList.remove('hidden');
|
||||
return;
|
||||
grid.innerHTML = '';
|
||||
emptyState.classList.add('hidden');
|
||||
noResultsState.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
emptyState.classList.add('hidden');
|
||||
noResultsState.classList.add('hidden');
|
||||
|
||||
if (currentGridView === 'list') {
|
||||
grid.className = 'space-y-4';
|
||||
grid.innerHTML = filteredPrinters.map(printer => this.createPrinterListItem(printer)).join('');
|
||||
} else {
|
||||
grid.className = 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6';
|
||||
grid.innerHTML = filteredPrinters.map(printer => this.createPrinterCard(printer)).join('');
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
createPrinterCard(printer) {
|
||||
const statusClasses = this.getStatusClasses(printer.status);
|
||||
const temperature = printer.temperature || {};
|
||||
const currentJob = printer.current_job || {};
|
||||
|
||||
return `
|
||||
<div class="printer-card ${statusClasses.container} p-6" data-printer-id="${printer.id}">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-12 h-12 ${statusClasses.iconBg} rounded-xl flex items-center justify-center">
|
||||
${this.getStatusIcon(printer.status)}
|
||||
</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>
|
||||
<h3 class="text-lg font-semibold text-mercedes-black dark:text-white">${printer.name}</h3>
|
||||
<p class="text-sm text-mercedes-gray dark:text-slate-400">${printer.model || 'Unbekanntes Modell'}</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium ${statusClasses.badge}">
|
||||
${this.getStatusText(printer.status)}
|
||||
</span>
|
||||
</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>
|
||||
|
||||
${printer.status === 'printing' && currentJob.progress ? `
|
||||
<div class="mb-4">
|
||||
<div class="flex justify-between text-sm text-mercedes-gray dark:text-slate-400 mb-2">
|
||||
<span>Fortschritt: ${currentJob.name || 'Unbekannter Job'}</span>
|
||||
<span>${currentJob.progress}%</span>
|
||||
</div>
|
||||
<div class="print-progress">
|
||||
<div class="print-progress-bar" style="width: ${currentJob.progress}%"></div>
|
||||
</div>
|
||||
${currentJob.time_remaining ? `
|
||||
<div class="text-xs text-mercedes-gray dark:text-slate-400 mt-1">
|
||||
Verbleibend: ${this.formatDuration(currentJob.time_remaining)}
|
||||
</div>
|
||||
` : ''}
|
||||
</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 class="grid grid-cols-2 gap-4 mb-4 text-sm">
|
||||
<div>
|
||||
<span class="text-mercedes-gray dark:text-slate-400">Standort:</span>
|
||||
<p class="text-mercedes-black dark:text-white truncate">${printer.location || 'Nicht angegeben'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-mercedes-gray dark:text-slate-400">IP:</span>
|
||||
<p class="text-mercedes-black dark:text-white">${printer.ip_address || 'N/A'}</p>
|
||||
</div>
|
||||
${Object.keys(temperature).length > 0 ? `
|
||||
<div>
|
||||
<span class="text-mercedes-gray dark:text-slate-400">Düse:</span>
|
||||
<p class="text-mercedes-black dark:text-white">${temperature.nozzle || 0}°C</p>
|
||||
</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>
|
||||
<span class="text-mercedes-gray dark:text-slate-400">Bett:</span>
|
||||
<p class="text-mercedes-black dark:text-white">${temperature.bed || 0}°C</p>
|
||||
</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>
|
||||
<span class="text-mercedes-gray dark:text-slate-400">Letzter Job:</span>
|
||||
<p class="text-mercedes-black dark:text-white truncate">${printer.last_job || 'Kein Job'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-mercedes-gray dark:text-slate-400">Uptime:</span>
|
||||
<p class="text-mercedes-black dark:text-white">${this.formatUptime(printer.uptime)}</p>
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<button onclick="printerManager.openPrinterDetails('${printer.id}')"
|
||||
class="printer-action-btn btn-details flex-1">
|
||||
<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="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
||||
</svg>
|
||||
Details
|
||||
</button>
|
||||
<button onclick="printerManager.editPrinter('${printer.id}')"
|
||||
class="printer-action-btn btn-edit flex-1">
|
||||
<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="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
|
||||
</svg>
|
||||
Bearbeiten
|
||||
</button>
|
||||
${this.getPrinterActionButton(printer)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
createPrinterListItem(printer) {
|
||||
const statusClasses = this.getStatusClasses(printer.status);
|
||||
|
||||
return `
|
||||
<div class="printer-card ${statusClasses.container} p-4" data-printer-id="${printer.id}">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="w-10 h-10 ${statusClasses.iconBg} rounded-lg flex items-center justify-center">
|
||||
${this.getStatusIcon(printer.status)}
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h3 class="text-lg font-semibold text-mercedes-black dark:text-white">${printer.name}</h3>
|
||||
<div class="flex items-center gap-4 text-sm text-mercedes-gray dark:text-slate-400">
|
||||
<span>${printer.model || 'Unbekanntes Modell'}</span>
|
||||
<span>•</span>
|
||||
<span>${printer.location || 'Kein Standort'}</span>
|
||||
<span>•</span>
|
||||
<span>${printer.ip_address || 'Keine IP'}</span>
|
||||
</div>
|
||||
</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 class="flex items-center gap-3">
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium ${statusClasses.badge}">
|
||||
${this.getStatusText(printer.status)}
|
||||
</span>
|
||||
<div class="flex gap-2">
|
||||
<button onclick="printerManager.openPrinterDetails('${printer.id}')"
|
||||
class="printer-action-btn btn-details">
|
||||
Details
|
||||
</button>
|
||||
<button onclick="printerManager.editPrinter('${printer.id}')"
|
||||
class="printer-action-btn btn-edit">
|
||||
Bearbeiten
|
||||
</button>
|
||||
</div>
|
||||
</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'
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
getPrinterActionButton(printer) {
|
||||
switch (printer.status) {
|
||||
case 'online':
|
||||
case 'idle':
|
||||
return `
|
||||
<button onclick="printerManager.testPrint('${printer.id}')"
|
||||
class="printer-action-btn btn-start">
|
||||
<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="M14.828 14.828a4 4 0 01-5.656 0M9 10h1.586a1 1 0 01.707.293l2.414 2.414a1 1 0 00.707.293H15M9 10V9a2 2 0 012-2h2a2 2 0 012 2v1M9 10v4a2 2 0 002 2h2a2 2 0 002-2v-4"/>
|
||||
</svg>
|
||||
Test
|
||||
</button>
|
||||
`;
|
||||
case 'printing':
|
||||
return `
|
||||
<button onclick="printerManager.pausePrint('${printer.id}')"
|
||||
class="printer-action-btn btn-pause">
|
||||
<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="M10 9v6m4-6v6"/>
|
||||
</svg>
|
||||
Pause
|
||||
</button>
|
||||
`;
|
||||
case 'error':
|
||||
return `
|
||||
<button onclick="printerManager.resetPrinter('${printer.id}')"
|
||||
class="printer-action-btn btn-maintenance">
|
||||
<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 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>
|
||||
Reset
|
||||
</button>
|
||||
`;
|
||||
default:
|
||||
return `
|
||||
<button onclick="printerManager.connectPrinter('${printer.id}')"
|
||||
class="printer-action-btn btn-start">
|
||||
<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="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"/>
|
||||
</svg>
|
||||
Verbinden
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
};
|
||||
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');
|
||||
getStatusClasses(status) {
|
||||
const classes = {
|
||||
'online': {
|
||||
container: 'status-online',
|
||||
iconBg: 'bg-green-100 dark:bg-green-900/30 text-green-600',
|
||||
badge: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'
|
||||
},
|
||||
'offline': {
|
||||
container: 'status-offline',
|
||||
iconBg: 'bg-red-100 dark:bg-red-900/30 text-red-600',
|
||||
badge: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400'
|
||||
},
|
||||
'printing': {
|
||||
container: 'status-printing',
|
||||
iconBg: 'bg-blue-100 dark:bg-blue-900/30 text-blue-600',
|
||||
badge: 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400'
|
||||
},
|
||||
'error': {
|
||||
container: 'status-error',
|
||||
iconBg: 'bg-orange-100 dark:bg-orange-900/30 text-orange-600',
|
||||
badge: 'bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-400'
|
||||
},
|
||||
'maintenance': {
|
||||
container: 'status-maintenance',
|
||||
iconBg: 'bg-purple-100 dark:bg-purple-900/30 text-purple-600',
|
||||
badge: 'bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-400'
|
||||
}
|
||||
};
|
||||
return classes[status] || classes['offline'];
|
||||
}
|
||||
|
||||
statusFilter.addEventListener('change', applyFilters);
|
||||
locationFilter.addEventListener('change', applyFilters);
|
||||
}
|
||||
|
||||
// Apply filters
|
||||
function applyFilters() {
|
||||
const statusFilter = document.getElementById('filterStatus').value;
|
||||
const locationFilter = document.getElementById('filterLocation').value;
|
||||
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 status-pulse"></div>',
|
||||
'error': '<div class="w-3 h-3 bg-orange-500 rounded-full"></div>',
|
||||
'maintenance': '<div class="w-3 h-3 bg-purple-500 rounded-full"></div>'
|
||||
};
|
||||
return icons[status] || icons['offline'];
|
||||
}
|
||||
|
||||
filteredPrinters = allPrinters.filter(printer => {
|
||||
const statusMatch = !statusFilter || printer.status === statusFilter;
|
||||
const locationMatch = !locationFilter || printer.location === locationFilter;
|
||||
return statusMatch && locationMatch;
|
||||
});
|
||||
getStatusText(status) {
|
||||
const texts = {
|
||||
'online': 'Online',
|
||||
'offline': 'Offline',
|
||||
'printing': 'Druckt',
|
||||
'error': 'Fehler',
|
||||
'maintenance': 'Wartung',
|
||||
'idle': 'Bereit'
|
||||
};
|
||||
return texts[status] || status;
|
||||
}
|
||||
|
||||
displayPrinters();
|
||||
updateStatistics();
|
||||
}
|
||||
|
||||
// Populate location filter
|
||||
function populateLocationFilter() {
|
||||
const locationFilter = document.getElementById('filterLocation');
|
||||
const locations = [...new Set(allPrinters.map(p => p.location).filter(Boolean))];
|
||||
updateStatistics() {
|
||||
const stats = {
|
||||
total: allPrinters.length,
|
||||
online: allPrinters.filter(p => ['online', 'idle'].includes(p.status)).length,
|
||||
offline: allPrinters.filter(p => p.status === 'offline').length,
|
||||
printing: allPrinters.filter(p => p.status === 'printing').length
|
||||
};
|
||||
|
||||
// Animate counter updates
|
||||
this.animateCounter('total-count', stats.total);
|
||||
this.animateCounter('online-count', stats.online);
|
||||
this.animateCounter('offline-count', stats.offline);
|
||||
this.animateCounter('printing-count', stats.printing);
|
||||
|
||||
// Update percentages
|
||||
const total = stats.total || 1;
|
||||
document.getElementById('online-percentage').textContent = Math.round((stats.online / total) * 100);
|
||||
document.getElementById('offline-percentage').textContent = Math.round((stats.offline / total) * 100);
|
||||
document.getElementById('active-percentage').textContent = Math.round((stats.printing / total) * 100);
|
||||
|
||||
this.updatePerformanceMetrics(stats);
|
||||
}
|
||||
|
||||
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');
|
||||
updatePerformanceMetrics(stats) {
|
||||
// Update utilization
|
||||
const utilization = stats.total > 0 ? Math.round((stats.printing / stats.total) * 100) : 0;
|
||||
document.getElementById('avg-utilization').textContent = utilization + '%';
|
||||
document.getElementById('utilization-bar').style.width = utilization + '%';
|
||||
|
||||
// Update active jobs count
|
||||
document.getElementById('active-jobs-count').textContent = stats.printing;
|
||||
|
||||
// Update queue count (placeholder)
|
||||
document.getElementById('queue-count').textContent = '0';
|
||||
|
||||
// Update time estimates (placeholder)
|
||||
document.getElementById('estimated-completion').textContent = stats.printing > 0 ? '2h 30min' : '--:--';
|
||||
document.getElementById('next-job-start').textContent = '--:--';
|
||||
}
|
||||
|
||||
document.getElementById('printerModalTitle').textContent = 'Drucker hinzufügen';
|
||||
document.getElementById('printerForm').reset();
|
||||
document.getElementById('printerId').value = '';
|
||||
document.getElementById('deletePrinterBtn').style.display = 'none';
|
||||
animateCounter(elementId, targetValue) {
|
||||
const element = document.getElementById(elementId);
|
||||
const currentValue = parseInt(element.textContent) || 0;
|
||||
const duration = 1000;
|
||||
const steps = 30;
|
||||
const stepValue = (targetValue - currentValue) / steps;
|
||||
let currentStep = 0;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
currentStep++;
|
||||
const newValue = Math.round(currentValue + (stepValue * currentStep));
|
||||
element.textContent = newValue;
|
||||
|
||||
if (currentStep >= steps) {
|
||||
element.textContent = targetValue;
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, duration / steps);
|
||||
}
|
||||
|
||||
modal.classList.remove('hidden');
|
||||
populateFilterDropdowns() {
|
||||
// Populate location filter
|
||||
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('');
|
||||
|
||||
// Populate model filter
|
||||
const modelFilter = document.getElementById('filterModel');
|
||||
const models = [...new Set(allPrinters.map(p => p.model).filter(Boolean))];
|
||||
modelFilter.innerHTML = '<option value="">Alle Modelle</option>' +
|
||||
models.map(model => `<option value="${model}">${model}</option>`).join('');
|
||||
}
|
||||
|
||||
updateFilterCounts() {
|
||||
document.getElementById('filtered-count').textContent = filteredPrinters.length;
|
||||
document.getElementById('total-printers').textContent = allPrinters.length;
|
||||
}
|
||||
|
||||
updateLastUpdateTime() {
|
||||
const now = new Date();
|
||||
document.getElementById('last-update-time').textContent = now.toLocaleTimeString('de-DE');
|
||||
document.getElementById('last-update').textContent = `Letzte Aktualisierung: ${now.toLocaleTimeString('de-DE')}`;
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
formatDuration(minutes) {
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const mins = minutes % 60;
|
||||
return hours > 0 ? `${hours}h ${mins}min` : `${mins}min`;
|
||||
}
|
||||
|
||||
formatUptime(uptime) {
|
||||
if (!uptime) return 'N/A';
|
||||
const days = Math.floor(uptime / 1440);
|
||||
const hours = Math.floor((uptime % 1440) / 60);
|
||||
return days > 0 ? `${days}d ${hours}h` : `${hours}h`;
|
||||
}
|
||||
|
||||
debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
|
||||
// Auto-refresh functionality
|
||||
startAutoRefresh() {
|
||||
if (isAutoRefreshEnabled) {
|
||||
autoRefreshInterval = setInterval(() => {
|
||||
this.loadPrinters();
|
||||
}, 30000); // Refresh every 30 seconds
|
||||
}
|
||||
}
|
||||
|
||||
stopAutoRefresh() {
|
||||
if (autoRefreshInterval) {
|
||||
clearInterval(autoRefreshInterval);
|
||||
autoRefreshInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
console.error(message);
|
||||
// Placeholder for toast notification system
|
||||
}
|
||||
|
||||
showSuccess(message) {
|
||||
console.log(message);
|
||||
// Placeholder for toast notification system
|
||||
}
|
||||
|
||||
closeAllModals() {
|
||||
const modals = ['printerModal', 'printerDetailsModal'];
|
||||
modals.forEach(modalId => {
|
||||
const modal = document.getElementById(modalId);
|
||||
if (modal && !modal.classList.contains('hidden')) {
|
||||
this.closeModal(modalId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
closeModal(modalId) {
|
||||
const modal = document.getElementById(modalId);
|
||||
const content = modal.querySelector('.mercedes-modal, .dashboard-card');
|
||||
|
||||
content.classList.remove('scale-100', 'opacity-100');
|
||||
content.classList.add('scale-95', 'opacity-0');
|
||||
setTimeout(() => {
|
||||
modalContent.classList.remove('scale-95', 'opacity-0');
|
||||
modalContent.classList.add('scale-100', 'opacity-100');
|
||||
}, 10);
|
||||
modal.classList.add('hidden');
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize Printer Manager
|
||||
let printerManager;
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
printerManager = new PrinterManager();
|
||||
});
|
||||
|
||||
// Global functions for UI interactions
|
||||
function refreshPrinters() {
|
||||
const button = document.getElementById('refresh-button');
|
||||
button.disabled = true;
|
||||
button.querySelector('svg').classList.add('animate-spin');
|
||||
|
||||
printerManager.loadPrinters().finally(() => {
|
||||
button.disabled = false;
|
||||
button.querySelector('svg').classList.remove('animate-spin');
|
||||
});
|
||||
}
|
||||
|
||||
function toggleAutoRefresh() {
|
||||
isAutoRefreshEnabled = !isAutoRefreshEnabled;
|
||||
const button = document.getElementById('auto-refresh-toggle');
|
||||
|
||||
if (isAutoRefreshEnabled) {
|
||||
button.textContent = 'Auto-Refresh: ON';
|
||||
button.classList.remove('bg-gray-500');
|
||||
button.classList.add('bg-mercedes-blue');
|
||||
printerManager.startAutoRefresh();
|
||||
} else {
|
||||
button.textContent = 'Auto-Refresh: OFF';
|
||||
button.classList.remove('bg-mercedes-blue');
|
||||
button.classList.add('bg-gray-500');
|
||||
printerManager.stopAutoRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
function toggleMaintenanceMode() {
|
||||
isMaintenanceMode = !isMaintenanceMode;
|
||||
const button = document.getElementById('maintenance-toggle');
|
||||
|
||||
if (isMaintenanceMode) {
|
||||
button.classList.add('bg-orange-500', 'text-white');
|
||||
button.querySelector('span').textContent = 'Wartung aktiv';
|
||||
} else {
|
||||
button.classList.remove('bg-orange-500', 'text-white');
|
||||
button.querySelector('span').textContent = 'Wartungsmodus';
|
||||
}
|
||||
}
|
||||
|
||||
function toggleGridView(view) {
|
||||
currentGridView = view;
|
||||
const gridBtn = document.getElementById('grid-view-btn');
|
||||
const listBtn = document.getElementById('list-view-btn');
|
||||
|
||||
if (view === 'grid') {
|
||||
gridBtn.classList.add('bg-mercedes-blue', 'text-white');
|
||||
gridBtn.classList.remove('text-mercedes-gray', 'hover:bg-mercedes-silver');
|
||||
listBtn.classList.remove('bg-mercedes-blue', 'text-white');
|
||||
listBtn.classList.add('text-mercedes-gray', 'hover:bg-mercedes-silver');
|
||||
} else {
|
||||
listBtn.classList.add('bg-mercedes-blue', 'text-white');
|
||||
listBtn.classList.remove('text-mercedes-gray', 'hover:bg-mercedes-silver');
|
||||
gridBtn.classList.remove('bg-mercedes-blue', 'text-white');
|
||||
gridBtn.classList.add('text-mercedes-gray', 'hover:bg-mercedes-silver');
|
||||
}
|
||||
|
||||
printerManager.displayPrinters();
|
||||
}
|
||||
|
||||
function clearAllFilters() {
|
||||
document.getElementById('search-input').value = '';
|
||||
document.getElementById('filterStatus').value = '';
|
||||
document.getElementById('filterLocation').value = '';
|
||||
document.getElementById('filterModel').value = '';
|
||||
document.getElementById('show-offline').checked = false;
|
||||
document.getElementById('show-maintenance').checked = false;
|
||||
document.getElementById('only-available').checked = false;
|
||||
document.getElementById('sort-by').value = 'name';
|
||||
|
||||
printerManager.applyFilters();
|
||||
}
|
||||
|
||||
function openAddPrinterModal() {
|
||||
printerManager.openAddPrinterModal();
|
||||
}
|
||||
|
||||
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);
|
||||
printerManager.closeModal('printerModal');
|
||||
}
|
||||
|
||||
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);
|
||||
printerManager.closeModal('printerDetailsModal');
|
||||
}
|
||||
|
||||
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 %}
|
@ -74,8 +74,8 @@ def test_database_schema():
|
||||
print_status("=== TESTE DATENBANK-SCHEMA ===", "INFO")
|
||||
|
||||
try:
|
||||
# Verbindung zur Datenbank
|
||||
db_path = "database.db"
|
||||
# Verbindung zur Datenbank - korrigierter Pfad
|
||||
db_path = "database/myp.db"
|
||||
if not os.path.exists(db_path):
|
||||
print_status("✗ Datenbank nicht gefunden", "ERROR")
|
||||
return False
|
||||
|
Loading…
x
Reference in New Issue
Block a user