📝 "Fix: Resolve issues with database shm andwal files in FEHLER_BEHOBEN.md, app.py, and templates/jobs.html (#123)"

This commit is contained in:
2025-05-30 19:21:15 +02:00
parent f8be70e1f7
commit 4d7d057805
5 changed files with 858 additions and 466 deletions

View File

@@ -881,7 +881,6 @@
{% block scripts %}
<script>
// Globale Variablen
window.isAdmin = {% if current_user.is_admin %}true{% else %}false{% endif %};
let jobsData = [];
let filteredJobs = [];
let currentPage = 1;
@@ -891,6 +890,9 @@ let selectedJobs = new Set();
let refreshInterval;
let lastUpdateTime;
// Benutzer-spezifische Konfiguration
window.isAdmin = {% if current_user.is_admin %}true{% else %}false{% endif %};
// Job Management System
class JobManager {
constructor() {
@@ -964,13 +966,25 @@ class JobManager {
async loadPrinters() {
try {
const response = await fetch('/api/printers');
if (!response.ok) {
console.error(`HTTP Error ${response.status}: ${response.statusText}`);
this.showError(`Fehler beim Laden der Drucker: ${response.statusText}`);
return;
}
const data = await response.json();
if (data.success) {
this.populatePrinterDropdowns(data.printers || []);
if (data.success && data.printers) {
this.populatePrinterDropdowns(data.printers);
console.log(`${data.printers.length} Drucker erfolgreich geladen`);
} else {
console.error('API Response structure:', data);
this.showError('Fehler beim Laden der Drucker: Unerwartete Response-Struktur');
}
} catch (error) {
console.error('Error loading printers:', error);
this.showError(`Fehler beim Laden der Drucker: ${error.message}`);
}
}
@@ -1393,6 +1407,8 @@ class JobManager {
animateCounter(elementId, targetValue) {
const element = document.getElementById(elementId);
if (!element) return;
const currentValue = parseInt(element.textContent) || 0;
const duration = 1000;
const steps = 30;
@@ -1449,14 +1465,50 @@ class JobManager {
};
}
showError(message) {
// Implementation für Error Display
console.error(message);
initializeStatistics() {
// Initialize any statistics-related functionality
console.log('Statistics initialized');
}
showSuccess(message) {
// Implementation für Success Display
console.log(message);
updatePagination() {
const totalPages = Math.ceil(filteredJobs.length / itemsPerPage);
const paginationContainer = document.getElementById('pagination-container');
if (totalPages <= 1) {
paginationContainer.classList.add('hidden');
return;
}
paginationContainer.classList.remove('hidden');
// Update page info
const pageInfo = document.getElementById('page-info');
const startIndex = (currentPage - 1) * itemsPerPage + 1;
const endIndex = Math.min(currentPage * itemsPerPage, filteredJobs.length);
pageInfo.textContent = `${startIndex}-${endIndex} von ${filteredJobs.length}`;
// Update navigation buttons
document.getElementById('prev-page').disabled = currentPage === 1;
document.getElementById('next-page').disabled = currentPage === totalPages;
}
toggleJobSelection(jobId) {
if (selectedJobs.has(jobId)) {
selectedJobs.delete(jobId);
} else {
selectedJobs.add(jobId);
}
// Update selected count
document.getElementById('selected-count').textContent = selectedJobs.size;
// Show/hide batch actions
const batchActions = document.getElementById('batch-actions');
if (selectedJobs.size > 0) {
batchActions.classList.remove('hidden');
} else {
batchActions.classList.add('hidden');
}
}
closeAllModals() {
@@ -1468,6 +1520,325 @@ class JobManager {
}
});
}
// File handling methods
handleDragOver(e) {
e.preventDefault();
e.target.classList.add('drag-over');
}
handleDragLeave(e) {
e.preventDefault();
e.target.classList.remove('drag-over');
}
handleFileDrop(e) {
e.preventDefault();
e.target.classList.remove('drag-over');
const files = e.dataTransfer.files;
if (files.length > 0) {
this.handleFileSelect({target: {files: files}});
}
}
handleFileSelect(e) {
const file = e.target.files[0];
if (file) {
const preview = document.getElementById('file-preview');
preview.textContent = `Ausgewählte Datei: ${file.name} (${(file.size / 1024 / 1024).toFixed(2)} MB)`;
preview.classList.remove('hidden');
}
}
// ===== JOB MANAGEMENT ACTIONS =====
async startJob(jobId) {
try {
const response = await fetch(`/api/jobs/${jobId}/start`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.getCSRFToken()
}
});
const data = await response.json();
if (data.success) {
this.showSuccess(`Job erfolgreich gestartet: ${data.message}`);
this.loadJobs(); // Refresh job list
} else {
this.showError(`Fehler beim Starten: ${data.error}`);
}
} catch (error) {
console.error('Error starting job:', error);
this.showError('Fehler beim Starten des Jobs');
}
}
async pauseJob(jobId) {
try {
const response = await fetch(`/api/jobs/${jobId}/pause`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.getCSRFToken()
}
});
const data = await response.json();
if (data.success) {
this.showSuccess(`Job erfolgreich pausiert: ${data.message}`);
this.loadJobs(); // Refresh job list
} else {
this.showError(`Fehler beim Pausieren: ${data.error}`);
}
} catch (error) {
console.error('Error pausing job:', error);
this.showError('Fehler beim Pausieren des Jobs');
}
}
async resumeJob(jobId) {
try {
const response = await fetch(`/api/jobs/${jobId}/resume`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.getCSRFToken()
}
});
const data = await response.json();
if (data.success) {
this.showSuccess(`Job erfolgreich fortgesetzt: ${data.message}`);
this.loadJobs(); // Refresh job list
} else {
this.showError(`Fehler beim Fortsetzen: ${data.error}`);
}
} catch (error) {
console.error('Error resuming job:', error);
this.showError('Fehler beim Fortsetzen des Jobs');
}
}
async deleteJob(jobId) {
if (!confirm('Sind Sie sicher, dass Sie diesen Job löschen möchten?')) {
return;
}
try {
const response = await fetch(`/api/jobs/${jobId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.getCSRFToken()
}
});
const data = await response.json();
if (data.success) {
this.showSuccess('Job erfolgreich gelöscht');
this.loadJobs(); // Refresh job list
} else {
this.showError(`Fehler beim Löschen: ${data.error}`);
}
} catch (error) {
console.error('Error deleting job:', error);
this.showError('Fehler beim Löschen des Jobs');
}
}
// ===== FORM HANDLERS =====
setupFormHandlers() {
// Quick Reservation Form Handler
const quickForm = document.getElementById('quickReservationForm');
if (quickForm) {
quickForm.addEventListener('submit', this.handleQuickReservation.bind(this));
}
// Main Job Form Handler
const mainForm = document.getElementById('newJobForm');
if (mainForm) {
mainForm.addEventListener('submit', this.handleJobSubmit.bind(this));
}
// Set default start time to now
const startTimeInput = document.getElementById('quick-start-time');
if (startTimeInput) {
const now = new Date();
now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
startTimeInput.value = now.toISOString().slice(0, 16);
}
const mainStartTimeInput = document.getElementById('start_time');
if (mainStartTimeInput) {
const now = new Date();
now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
mainStartTimeInput.value = now.toISOString().slice(0, 16);
}
}
async handleQuickReservation(e) {
e.preventDefault();
const formData = new FormData(e.target);
const jobData = {
printer_id: parseInt(formData.get('printer_id')),
start_iso: formData.get('start_time'),
duration_minutes: parseInt(formData.get('duration')),
name: formData.get('title') || 'Schnell-Reservierung'
};
// Validierung
if (!jobData.printer_id) {
this.showError('Bitte wählen Sie einen Drucker aus');
return;
}
if (!jobData.start_iso) {
this.showError('Bitte geben Sie eine Startzeit an');
return;
}
if (!jobData.duration_minutes || jobData.duration_minutes < 15) {
this.showError('Dauer muss mindestens 15 Minuten betragen');
return;
}
try {
const response = await fetch('/api/jobs', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.getCSRFToken()
},
body: JSON.stringify(jobData)
});
const data = await response.json();
if (data.success) {
this.showSuccess('Schnell-Reservierung erfolgreich erstellt!');
closeQuickReservationModal();
this.loadJobs(); // Refresh job list
// Reset form
e.target.reset();
// Show additional info if immediate start
if (data.immediate_start) {
setTimeout(() => {
this.showSuccess('Job wurde sofort gestartet und Drucker eingeschaltet!');
}, 1000);
}
} else {
this.showError(`Fehler beim Erstellen: ${data.error}`);
}
} catch (error) {
console.error('Error creating quick reservation:', error);
this.showError('Fehler beim Erstellen der Reservierung');
}
}
async handleJobSubmit(e) {
e.preventDefault();
const formData = new FormData(e.target);
const jobData = {
printer_id: parseInt(formData.get('printer_id')),
start_iso: formData.get('start_time'),
duration_minutes: parseInt(formData.get('duration')),
name: formData.get('job_title') || 'Neuer Druckjob'
};
// File upload handling (optional)
const fileInput = document.getElementById('stl_file');
if (fileInput.files.length > 0) {
// TODO: Implement file upload
console.log('File selected:', fileInput.files[0]);
}
try {
const response = await fetch('/api/jobs', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': this.getCSRFToken()
},
body: JSON.stringify(jobData)
});
const data = await response.json();
if (data.success) {
this.showSuccess('Job erfolgreich erstellt!');
this.loadJobs(); // Refresh job list
// Reset form
e.target.reset();
// Collapse form
toggleFormExpansion();
} else {
this.showError(`Fehler beim Erstellen: ${data.error}`);
}
} catch (error) {
console.error('Error creating job:', error);
this.showError('Fehler beim Erstellen des Jobs');
}
}
// ===== UTILITY FUNCTIONS =====
getCSRFToken() {
const token = document.querySelector('meta[name=csrf-token]');
return token ? token.getAttribute('content') : '';
}
showSuccess(message) {
// Create and show success toast
this.showToast(message, 'success');
}
showError(message) {
// Create and show error toast
this.showToast(message, 'error');
}
showToast(message, type = 'info') {
// Simple toast implementation
const toast = document.createElement('div');
toast.className = `fixed top-4 right-4 p-4 rounded-lg shadow-lg z-50 ${
type === 'success' ? 'bg-green-500 text-white' :
type === 'error' ? 'bg-red-500 text-white' :
'bg-blue-500 text-white'
}`;
toast.textContent = message;
document.body.appendChild(toast);
// Animate in
setTimeout(() => {
toast.style.transform = 'translateX(0)';
toast.style.opacity = '1';
}, 10);
// Remove after 5 seconds
setTimeout(() => {
toast.style.transform = 'translateX(100%)';
toast.style.opacity = '0';
setTimeout(() => {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
}, 300);
}, 5000);
}
}
// Modal Animations