- Anzeige der verbleibenden Zeit für Druckaufträge im Frontend - Neue UI-Komponenten für Wartejobs und deren Freischaltung - Freischaltungs-Modal für wartende Jobs implementiert - Drucker-Status wird beim Erstellen eines Auftrags angezeigt - Checkbox für Warteschlangen-Option beim Erstellen eines Auftrags 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
443 lines
21 KiB
HTML
443 lines
21 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Druckaufträge - MYP API Tester{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="row">
|
|
<div class="col-md-12 mb-4">
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h4 class="mb-0">Druckaufträge verwalten</h4>
|
|
<button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#newJobForm">
|
|
Neuen Auftrag erstellen
|
|
</button>
|
|
</div>
|
|
<div class="collapse" id="newJobForm">
|
|
<div class="card-body border-bottom">
|
|
<form class="api-form" data-url="/api/jobs" data-method="POST" data-response="createJobResponse" data-reload="true">
|
|
<div class="mb-3">
|
|
<label for="jobPrinterId" class="form-label">Drucker</label>
|
|
<select class="form-control" id="jobPrinterId" name="printerId" required>
|
|
<option value="">Drucker auswählen...</option>
|
|
<!-- Wird dynamisch gefüllt -->
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="jobDuration" class="form-label">Dauer (Minuten)</label>
|
|
<input type="number" class="form-control" id="jobDuration" name="durationInMinutes" min="1" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="jobComments" class="form-label">Kommentare</label>
|
|
<textarea class="form-control" id="jobComments" name="comments" rows="3"></textarea>
|
|
</div>
|
|
<div class="mb-3 form-check">
|
|
<input type="checkbox" class="form-check-input" id="allowQueuedJobs" name="allowQueuedJobs" value="true">
|
|
<label class="form-check-label" for="allowQueuedJobs">
|
|
Auftrag in Warteschlange erlauben (wenn Drucker belegt ist)
|
|
</label>
|
|
</div>
|
|
<button type="submit" class="btn btn-success">Auftrag erstellen</button>
|
|
</form>
|
|
<div class="mt-3">
|
|
<h6>Antwort:</h6>
|
|
<pre class="api-response" id="createJobResponse"></pre>
|
|
</div>
|
|
</div>
|
|
</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">Aufträge aktualisieren</button>
|
|
</form>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Drucker</th>
|
|
<th>Benutzer</th>
|
|
<th>Start</th>
|
|
<th>Dauer (Min)</th>
|
|
<th>Verbleibend (Min)</th>
|
|
<th>Status</th>
|
|
<th>Kommentare</th>
|
|
<th>Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="jobsTableBody">
|
|
<!-- Wird dynamisch gefüllt -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div>
|
|
<h6>API-Antwort:</h6>
|
|
<pre class="api-response" id="jobsResponse"></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Job abbrechen Modal -->
|
|
<div class="modal fade" id="abortJobModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Auftrag abbrechen</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>Möchten Sie den Auftrag wirklich abbrechen?</p>
|
|
<form id="abortJobForm" class="api-form" data-method="POST" data-response="abortJobResponse" data-reload="true">
|
|
<input type="hidden" id="abortJobId" name="jobId">
|
|
<div class="mb-3">
|
|
<label for="abortReason" class="form-label">Abbruchgrund</label>
|
|
<textarea class="form-control" id="abortReason" name="reason" rows="3"></textarea>
|
|
</div>
|
|
</form>
|
|
<div class="mt-3">
|
|
<h6>Antwort:</h6>
|
|
<pre class="api-response" id="abortJobResponse"></pre>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
|
<button type="submit" form="abortJobForm" class="btn btn-danger">Auftrag abbrechen</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Job beenden Modal -->
|
|
<div class="modal fade" id="finishJobModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Auftrag beenden</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>Möchten Sie den Auftrag als beendet markieren?</p>
|
|
<form id="finishJobForm" class="api-form" data-method="POST" data-response="finishJobResponse" data-reload="true">
|
|
<input type="hidden" id="finishJobId" name="jobId">
|
|
</form>
|
|
<div class="mt-3">
|
|
<h6>Antwort:</h6>
|
|
<pre class="api-response" id="finishJobResponse"></pre>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
|
<button type="submit" form="finishJobForm" class="btn btn-success">Auftrag beenden</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Job verlängern Modal -->
|
|
<div class="modal fade" id="extendJobModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Auftrag verlängern</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="extendJobForm" class="api-form" data-method="POST" data-response="extendJobResponse" data-reload="true">
|
|
<input type="hidden" id="extendJobId" name="jobId">
|
|
<div class="mb-3">
|
|
<label for="extendHours" class="form-label">Stunden</label>
|
|
<input type="number" class="form-control" id="extendHours" name="hours" min="0" value="0">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="extendMinutes" class="form-label">Minuten</label>
|
|
<input type="number" class="form-control" id="extendMinutes" name="minutes" min="0" max="59" value="30">
|
|
</div>
|
|
</form>
|
|
<div class="mt-3">
|
|
<h6>Antwort:</h6>
|
|
<pre class="api-response" id="extendJobResponse"></pre>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
|
<button type="submit" form="extendJobForm" class="btn btn-primary">Auftrag verlängern</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Job Kommentare bearbeiten Modal -->
|
|
<div class="modal fade" id="editCommentsModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Kommentare bearbeiten</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="editCommentsForm" class="api-form" data-method="PUT" data-response="editCommentsResponse" data-reload="true">
|
|
<input type="hidden" id="editCommentsJobId" name="jobId">
|
|
<div class="mb-3">
|
|
<label for="editJobComments" class="form-label">Kommentare</label>
|
|
<textarea class="form-control" id="editJobComments" name="comments" rows="3"></textarea>
|
|
</div>
|
|
</form>
|
|
<div class="mt-3">
|
|
<h6>Antwort:</h6>
|
|
<pre class="api-response" id="editCommentsResponse"></pre>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
|
<button type="submit" form="editCommentsForm" class="btn btn-primary">Speichern</button>
|
|
</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() {
|
|
// Drucker für Dropdown laden
|
|
loadPrinters();
|
|
|
|
// Aufträge laden
|
|
document.querySelector('form[data-url="/api/jobs"]').dispatchEvent(new Event('submit'));
|
|
|
|
// Tabelle aktualisieren, wenn Aufträge geladen werden
|
|
const jobsResponse = document.getElementById('jobsResponse');
|
|
const observer = new MutationObserver(function(mutations) {
|
|
try {
|
|
const jobs = JSON.parse(jobsResponse.textContent);
|
|
updateJobsTable(jobs);
|
|
} catch (e) {
|
|
console.error('Fehler beim Parsen der Auftrags-Daten:', e);
|
|
}
|
|
});
|
|
|
|
observer.observe(jobsResponse, { childList: true, characterData: true, subtree: true });
|
|
|
|
// Abort-Modal vorbereiten
|
|
document.getElementById('abortJobModal').addEventListener('show.bs.modal', function(event) {
|
|
const button = event.relatedTarget;
|
|
const jobId = button.getAttribute('data-job-id');
|
|
|
|
document.getElementById('abortJobId').value = jobId;
|
|
document.getElementById('abortJobForm').setAttribute('data-url', `/api/jobs/${jobId}/abort`);
|
|
});
|
|
|
|
// Finish-Modal vorbereiten
|
|
document.getElementById('finishJobModal').addEventListener('show.bs.modal', function(event) {
|
|
const button = event.relatedTarget;
|
|
const jobId = button.getAttribute('data-job-id');
|
|
|
|
document.getElementById('finishJobId').value = jobId;
|
|
document.getElementById('finishJobForm').setAttribute('data-url', `/api/jobs/${jobId}/finish`);
|
|
});
|
|
|
|
// Extend-Modal vorbereiten
|
|
document.getElementById('extendJobModal').addEventListener('show.bs.modal', function(event) {
|
|
const button = event.relatedTarget;
|
|
const jobId = button.getAttribute('data-job-id');
|
|
|
|
document.getElementById('extendJobId').value = jobId;
|
|
document.getElementById('extendJobForm').setAttribute('data-url', `/api/jobs/${jobId}/extend`);
|
|
});
|
|
|
|
// Edit-Comments-Modal vorbereiten
|
|
document.getElementById('editCommentsModal').addEventListener('show.bs.modal', function(event) {
|
|
const button = event.relatedTarget;
|
|
const jobId = button.getAttribute('data-job-id');
|
|
const comments = button.getAttribute('data-job-comments');
|
|
|
|
document.getElementById('editCommentsJobId').value = jobId;
|
|
document.getElementById('editCommentsForm').setAttribute('data-url', `/api/jobs/${jobId}/comments`);
|
|
document.getElementById('editJobComments').value = comments || '';
|
|
});
|
|
|
|
// 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`);
|
|
});
|
|
});
|
|
|
|
async function loadPrinters() {
|
|
try {
|
|
const response = await fetch('/api/printers');
|
|
const printers = await response.json();
|
|
|
|
const selectElement = document.getElementById('jobPrinterId');
|
|
selectElement.innerHTML = '<option value="">Drucker auswählen...</option>';
|
|
|
|
// Drucker anzeigen (alle, da man jetzt auch für belegte Drucker Jobs erstellen kann)
|
|
printers.forEach(printer => {
|
|
const option = document.createElement('option');
|
|
option.value = printer.id;
|
|
|
|
// Status-Information zum Drucker hinzufügen
|
|
const statusText = printer.status === 0 ? '(Verfügbar)' : '(Belegt)';
|
|
option.textContent = `${printer.name} - ${printer.description} ${statusText}`;
|
|
|
|
// Belegte Drucker visuell unterscheiden
|
|
if (printer.status !== 0) {
|
|
option.classList.add('text-muted');
|
|
}
|
|
|
|
selectElement.appendChild(option);
|
|
});
|
|
|
|
// Hinweis auf die Checkbox für Warteschlange anzeigen oder verstecken
|
|
const allowQueuedJobsCheckbox = document.getElementById('allowQueuedJobs');
|
|
const queueCheckboxContainer = allowQueuedJobsCheckbox.closest('.form-check');
|
|
|
|
// Prüfen, ob es belegte Drucker gibt
|
|
const hasBusyPrinters = printers.some(printer => printer.status !== 0);
|
|
queueCheckboxContainer.style.display = hasBusyPrinters ? 'block' : 'none';
|
|
|
|
// Event-Listener für die Druckerauswahl hinzufügen
|
|
selectElement.addEventListener('change', function() {
|
|
const selectedPrinterId = this.value;
|
|
const selectedPrinter = printers.find(p => p.id === selectedPrinterId);
|
|
|
|
if (selectedPrinter && selectedPrinter.status !== 0) {
|
|
// Wenn ein belegter Drucker ausgewählt wird, Checkbox für Warteschlange anzeigen
|
|
queueCheckboxContainer.style.display = 'block';
|
|
allowQueuedJobsCheckbox.checked = true;
|
|
} else if (selectedPrinter && selectedPrinter.status === 0) {
|
|
// Wenn ein verfügbarer Drucker ausgewählt wird, Checkbox für Warteschlange verstecken
|
|
allowQueuedJobsCheckbox.checked = false;
|
|
}
|
|
});
|
|
} catch (e) {
|
|
console.error('Fehler beim Laden der Drucker:', e);
|
|
}
|
|
}
|
|
|
|
function updateJobsTable(jobs) {
|
|
const tableBody = document.getElementById('jobsTableBody');
|
|
tableBody.innerHTML = '';
|
|
|
|
jobs.forEach(job => {
|
|
const row = document.createElement('tr');
|
|
|
|
const startDate = new Date(job.startAt);
|
|
const formattedStart = startDate.toLocaleString();
|
|
|
|
const isActive = !job.aborted && job.remainingMinutes > 0 && !job.waitingApproval;
|
|
const isWaiting = !job.aborted && job.waitingApproval;
|
|
|
|
let statusText = '';
|
|
let statusClass = '';
|
|
|
|
if (job.aborted) {
|
|
statusText = 'Abgebrochen';
|
|
statusClass = 'text-danger';
|
|
} else if (job.waitingApproval) {
|
|
statusText = 'Wartet auf Freischaltung';
|
|
statusClass = 'text-info';
|
|
} else if (job.remainingMinutes <= 0) {
|
|
statusText = 'Abgeschlossen';
|
|
statusClass = 'text-success';
|
|
} else {
|
|
statusText = 'Aktiv';
|
|
statusClass = 'text-warning';
|
|
}
|
|
|
|
// Zeige die verbleibende Zeit an
|
|
const remainingTime = job.waitingApproval ? '-' : job.remainingMinutes;
|
|
|
|
row.innerHTML = `
|
|
<td>${job.id}</td>
|
|
<td>${job.printerId}</td>
|
|
<td>${job.userId}</td>
|
|
<td>${formattedStart}</td>
|
|
<td>${job.durationInMinutes}</td>
|
|
<td>${remainingTime}</td>
|
|
<td><span class="${statusClass}">${statusText}</span></td>
|
|
<td>${job.comments || '-'}</td>
|
|
<td>
|
|
${isActive ? `
|
|
<button type="button" class="btn btn-sm btn-danger mb-1"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#abortJobModal"
|
|
data-job-id="${job.id}">
|
|
Abbrechen
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-success mb-1"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#finishJobModal"
|
|
data-job-id="${job.id}">
|
|
Beenden
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-primary mb-1"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#extendJobModal"
|
|
data-job-id="${job.id}">
|
|
Verlängern
|
|
</button>
|
|
` : ''}
|
|
|
|
${isWaiting ? `
|
|
<button type="button" class="btn btn-sm btn-success mb-1"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#approveJobModal"
|
|
data-job-id="${job.id}">
|
|
Freischalten
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-danger mb-1"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#abortJobModal"
|
|
data-job-id="${job.id}">
|
|
Abbrechen
|
|
</button>
|
|
` : ''}
|
|
|
|
<button type="button" class="btn btn-sm btn-secondary mb-1"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#editCommentsModal"
|
|
data-job-id="${job.id}"
|
|
data-job-comments="${job.comments || ''}">
|
|
Kommentare
|
|
</button>
|
|
</td>
|
|
`;
|
|
|
|
tableBody.appendChild(row);
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %} |