{% extends "base.html" %} {% block title %}Dashboard - MYP API Tester{% endblock %} {% block content %} <div class="row"> <div class="col-md-12 mb-4"> <div class="card"> <div class="card-header"> <h4 class="mb-0">Willkommen, {{ current_user.display_name }}</h4> </div> <div class="card-body"> <p>Benutzerdetails:</p> <ul> <li><strong>ID:</strong> {{ current_user.id }}</li> <li><strong>Benutzername:</strong> {{ current_user.username }}</li> <li><strong>E-Mail:</strong> {{ current_user.email or "Nicht angegeben" }}</li> <li><strong>Rolle:</strong> {{ current_user.role }}</li> </ul> <div class="mt-3"> <a href="/admin/printers" class="btn btn-primary me-2">Drucker verwalten</a> <a href="/admin/jobs" class="btn btn-success me-2">Druckaufträge verwalten</a> {% if current_user.role == 'admin' %} <a href="/admin/users" class="btn btn-info me-2">Benutzer verwalten</a> <a href="/admin/stats" class="btn btn-secondary">Statistiken</a> {% endif %} </div> </div> </div> </div> </div> <div class="row"> <div class="col-md-6 mb-4"> <div class="card"> <div class="card-header"> <h5 class="mb-0">Aktive Druckaufträge</h5> </div> <div class="card-body"> <form class="api-form mb-3" data-url="/api/jobs" data-method="GET" data-response="jobsResponse"> <button type="submit" class="btn btn-primary">Aktualisieren</button> </form> <div id="activeJobsContainer"> <div class="alert alert-info">Lade Druckaufträge...</div> </div> <div class="d-none"> <h6>API-Antwort:</h6> <pre class="api-response" id="jobsResponse"></pre> </div> </div> </div> </div> <div class="col-md-6 mb-4"> <div class="card"> <div class="card-header"> <h5 class="mb-0">Verfügbare Drucker</h5> </div> <div class="card-body"> <form class="api-form mb-3" data-url="/api/printers" data-method="GET" data-response="printersResponse"> <button type="submit" class="btn btn-primary">Aktualisieren</button> </form> <div id="availablePrintersContainer"> <div class="alert alert-info">Lade Drucker...</div> </div> <div class="d-none"> <h6>API-Antwort:</h6> <pre class="api-response" id="printersResponse"></pre> </div> </div> </div> </div> </div> <!-- Job freischalten Modal --> <div class="modal fade" id="approveJobModal" tabindex="-1"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">Druckauftrag freischalten</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <p>Möchten Sie diesen Druckauftrag jetzt freischalten und starten?</p> <p><strong>Hinweis:</strong> Der Drucker muss verfügbar sein, damit der Auftrag gestartet werden kann.</p> <form id="approveJobForm" class="api-form" data-method="POST" data-response="approveJobResponse" data-reload="true"> <input type="hidden" id="approveJobId" name="jobId"> </form> <div class="mt-3"> <h6>Antwort:</h6> <pre class="api-response" id="approveJobResponse"></pre> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button> <button type="submit" form="approveJobForm" class="btn btn-success">Freischalten</button> </div> </div> </div> </div> {% endblock %} {% block scripts %} <script> document.addEventListener('DOMContentLoaded', function() { // Aufträge und Drucker laden document.querySelector('form[data-url="/api/jobs"]').dispatchEvent(new Event('submit')); document.querySelector('form[data-url="/api/printers"]').dispatchEvent(new Event('submit')); // Tabellen aktualisieren, wenn Daten geladen werden const jobsResponse = document.getElementById('jobsResponse'); const printersResponse = document.getElementById('printersResponse'); // Observer für Jobs const jobsObserver = new MutationObserver(function(mutations) { try { const jobs = JSON.parse(jobsResponse.textContent); updateActiveJobs(jobs); } catch (e) { console.error('Fehler beim Parsen der Auftrags-Daten:', e); } }); jobsObserver.observe(jobsResponse, { childList: true, characterData: true, subtree: true }); // Observer für Drucker const printersObserver = new MutationObserver(function(mutations) { try { const printers = JSON.parse(printersResponse.textContent); updateAvailablePrinters(printers); } catch (e) { console.error('Fehler beim Parsen der Drucker-Daten:', e); } }); printersObserver.observe(printersResponse, { childList: true, characterData: true, subtree: true }); // Approve-Modal vorbereiten document.getElementById('approveJobModal').addEventListener('show.bs.modal', function(event) { const button = event.relatedTarget; const jobId = button.getAttribute('data-job-id'); document.getElementById('approveJobId').value = jobId; document.getElementById('approveJobForm').setAttribute('data-url', `/api/jobs/${jobId}/approve`); }); // Automatische Aktualisierung alle 60 Sekunden setInterval(() => { document.querySelector('form[data-url="/api/jobs"]').dispatchEvent(new Event('submit')); document.querySelector('form[data-url="/api/printers"]').dispatchEvent(new Event('submit')); }, 60000); }); function updateActiveJobs(jobs) { const container = document.getElementById('activeJobsContainer'); // Filter für aktive und wartende Jobs const activeJobs = jobs.filter(job => !job.aborted && job.remainingMinutes > 0 && !job.waitingApproval); const waitingJobs = jobs.filter(job => !job.aborted && job.waitingApproval); if (activeJobs.length === 0 && waitingJobs.length === 0) { container.innerHTML = '<div class="alert alert-info">Keine aktiven Druckaufträge vorhanden.</div>'; return; } let html = ''; // Aktive Jobs anzeigen if (activeJobs.length > 0) { html += '<h6 class="mt-3">Laufende Aufträge</h6>'; html += '<div class="list-group mb-3">'; activeJobs.forEach(job => { // Prozentsatz der abgelaufenen Zeit berechnen const totalDuration = job.durationInMinutes; const elapsed = totalDuration - job.remainingMinutes; const percentage = Math.round((elapsed / totalDuration) * 100); html += ` <div class="list-group-item"> <div class="d-flex justify-content-between"> <div> <strong>Job ${job.id.substring(0, 8)}...</strong> (${job.durationInMinutes} Min) <div class="small text-muted">Verbleibend: ${job.remainingMinutes} Min</div> </div> <div> <span class="badge bg-warning">Aktiv</span> </div> </div> <div class="progress mt-2" style="height: 10px;"> <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: ${percentage}%;" aria-valuenow="${percentage}" aria-valuemin="0" aria-valuemax="100"> ${percentage}% </div> </div> </div> `; }); html += '</div>'; } // Wartende Jobs anzeigen if (waitingJobs.length > 0) { html += '<h6 class="mt-3">Wartende Aufträge</h6>'; html += '<div class="list-group">'; waitingJobs.forEach(job => { html += ` <div class="list-group-item"> <div class="d-flex justify-content-between"> <div> <strong>Job ${job.id.substring(0, 8)}...</strong> (${job.durationInMinutes} Min) <div class="small text-muted">Drucker: ${job.socketId.substring(0, 8)}...</div> </div> <div> <span class="badge bg-info">Wartet</span> </div> </div> <div class="mt-2"> <button type="button" class="btn btn-sm btn-success" data-bs-toggle="modal" data-bs-target="#approveJobModal" data-job-id="${job.id}"> Freischalten </button> </div> </div> `; }); html += '</div>'; } container.innerHTML = html; } function updateAvailablePrinters(printers) { const container = document.getElementById('availablePrintersContainer'); // Filter für verfügbare Drucker const availablePrinters = printers.filter(printer => printer.status === 0); if (availablePrinters.length === 0) { container.innerHTML = '<div class="alert alert-warning">Keine verfügbaren Drucker gefunden.</div>'; return; } let html = '<div class="list-group">'; availablePrinters.forEach(printer => { html += ` <div class="list-group-item"> <div class="d-flex justify-content-between align-items-center"> <div> <strong>${printer.name}</strong> <div class="small text-muted">${printer.description}</div> </div> <div> <span class="badge bg-success">Verfügbar</span> </div> </div> <div class="mt-2"> <a href="/admin/jobs" class="btn btn-sm btn-primary">Auftrag erstellen</a> </div> </div> `; }); html += '</div>'; container.innerHTML = html; // Gesamtstatistik hinzufügen const busyPrinters = printers.filter(printer => printer.status === 1).length; const totalPrinters = printers.length; if (totalPrinters > 0) { const statsHtml = ` <div class="mt-3"> <div class="d-flex justify-content-between"> <small>Verfügbar: ${availablePrinters.length} / ${totalPrinters}</small> <small>Belegt: ${busyPrinters} / ${totalPrinters}</small> </div> <div class="progress mt-1" style="height: 5px;"> <div class="progress-bar bg-success" style="width: ${(availablePrinters.length / totalPrinters) * 100}%"></div> <div class="progress-bar bg-warning" style="width: ${(busyPrinters / totalPrinters) * 100}%"></div> </div> </div> `; container.innerHTML += statsHtml; } } </script> {% endblock %}