Verbesserte Dashboard-Ansicht mit Echtzeit-Informationen
- Neue übersichtliche Dashboard-Ansicht für aktive und wartende Jobs - Fortschrittsbalken für laufende Druckaufträge mit verbleibender Zeit - Liste der verfügbaren Drucker mit schnellem Zugriff auf Auftragserstellung - Freischaltungs-Funktionalität für wartende Jobs direkt vom Dashboard - Automatische Aktualisierung der Daten alle 60 Sekunden 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
097934ac18
commit
ad5bd4367e
@ -34,15 +34,20 @@
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">API-Test: GET /api/me</h5>
|
||||
<h5 class="mb-0">Aktive Druckaufträge</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="api-form" data-url="/api/me" data-method="GET" data-response="meResponse">
|
||||
<button type="submit" class="btn btn-primary">API aufrufen</button>
|
||||
<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 class="mt-3">
|
||||
<h6>Antwort:</h6>
|
||||
<pre class="api-response" id="meResponse"></pre>
|
||||
|
||||
<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>
|
||||
@ -51,18 +56,249 @@
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">API-Test: GET /api/test</h5>
|
||||
<h5 class="mb-0">Verfügbare Drucker</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="api-form" data-url="/api/test" data-method="GET" data-response="testResponse">
|
||||
<button type="submit" class="btn btn-primary">API aufrufen</button>
|
||||
<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 class="mt-3">
|
||||
<h6>Antwort:</h6>
|
||||
<pre class="api-response" id="testResponse"></pre>
|
||||
|
||||
<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 %}
|
Loading…
x
Reference in New Issue
Block a user