feat: viewer local upload, Bambu collapse, planning done/fail, stats waste, portal link in jobs, sidebar collapsible, settings layout fix
Deploy via Portainer / deploy (push) Successful in 0s
Deploy via Portainer / deploy (push) Successful in 0s
This commit is contained in:
+72
-6
@@ -148,9 +148,20 @@
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="slotModalId">
|
||||
<p id="slotModalInfo" class="text-muted small"></p>
|
||||
|
||||
<!-- Quick action buttons -->
|
||||
<div class="d-flex gap-2 mb-3">
|
||||
<button class="btn btn-sm btn-success flex-fill" onclick="quickAction('done')">
|
||||
<i class="bi bi-check-circle me-1"></i>Terminé
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger flex-fill" onclick="quickAction('fail')">
|
||||
<i class="bi bi-exclamation-triangle me-1"></i>Échec
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">Statut</label>
|
||||
<select id="slotModalStatus" class="form-select">
|
||||
<select id="slotModalStatus" class="form-select" onchange="onStatusChange()">
|
||||
<option value="planned">Planifié</option>
|
||||
<option value="printing">En cours</option>
|
||||
<option value="done">Terminé ✓</option>
|
||||
@@ -159,9 +170,19 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3" id="slotModalNoteGroup">
|
||||
<label class="form-label">Note (optionnel)</label>
|
||||
<label class="form-label">Note d'échec (optionnel)</label>
|
||||
<input type="text" id="slotModalNote" class="form-control" placeholder="Ex: couche 45 — sous-extrusion">
|
||||
</div>
|
||||
<div class="mb-3" id="slotModalWastedGroup" style="display:none">
|
||||
<label class="form-label">Filament gaspillé (g) <small class="text-muted fw-normal">optionnel</small></label>
|
||||
<input type="number" id="slotModalWasted" class="form-control" step="0.1" min="0"
|
||||
placeholder="Poids plateau si vide">
|
||||
</div>
|
||||
<div class="mb-3" id="slotModalActualGroup" style="display:none">
|
||||
<label class="form-label">Poids réel consommé (g) <small class="text-muted fw-normal">optionnel</small></label>
|
||||
<input type="number" id="slotModalActual" class="form-control" step="0.1" min="0"
|
||||
placeholder="Pour déduire du stock">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer d-flex justify-content-between">
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="deleteSlot()">
|
||||
@@ -343,6 +364,13 @@ async function saveSlot() {
|
||||
}
|
||||
|
||||
let currentSlot = null;
|
||||
|
||||
function onStatusChange() {
|
||||
const status = document.getElementById('slotModalStatus').value;
|
||||
document.getElementById('slotModalWastedGroup').style.display = status === 'failed' ? '' : 'none';
|
||||
document.getElementById('slotModalActualGroup').style.display = status === 'done' ? '' : 'none';
|
||||
}
|
||||
|
||||
function openSlotModal(slot) {
|
||||
currentSlot = slot;
|
||||
document.getElementById('slotModalId').value = slot.id;
|
||||
@@ -350,17 +378,55 @@ function openSlotModal(slot) {
|
||||
`${slot.job_name} · ${slot.printer_name} · ${formatDt(slot.planned_start)}`;
|
||||
document.getElementById('slotModalStatus').value = slot.status || 'planned';
|
||||
document.getElementById('slotModalNote').value = slot.failure_note || '';
|
||||
document.getElementById('slotModalWasted').value = '';
|
||||
document.getElementById('slotModalActual').value = '';
|
||||
onStatusChange();
|
||||
new bootstrap.Modal(document.getElementById('slotModal')).show();
|
||||
}
|
||||
|
||||
async function quickAction(type) {
|
||||
// Submit immediately via the dedicated fail/done endpoints
|
||||
const id = document.getElementById('slotModalId').value;
|
||||
const note = document.getElementById('slotModalNote').value;
|
||||
const wasted_g = document.getElementById('slotModalWasted').value || '';
|
||||
const actual_g = document.getElementById('slotModalActual').value || '';
|
||||
|
||||
if (type === 'done') {
|
||||
const fd = new FormData();
|
||||
fd.append('actual_g', actual_g);
|
||||
await fetch(`/api/slots/${id}/done`, {method:'POST', body:fd});
|
||||
} else {
|
||||
const fd = new FormData();
|
||||
fd.append('failure_note', note);
|
||||
fd.append('wasted_g', wasted_g);
|
||||
await fetch(`/api/slots/${id}/fail`, {method:'POST', body:fd});
|
||||
}
|
||||
bootstrap.Modal.getInstance(document.getElementById('slotModal')).hide();
|
||||
location.reload();
|
||||
}
|
||||
|
||||
async function updateSlotStatus() {
|
||||
const id = document.getElementById('slotModalId').value;
|
||||
const status = document.getElementById('slotModalStatus').value;
|
||||
const note = document.getElementById('slotModalNote').value;
|
||||
await fetch(`/api/slots/${id}/status`, {
|
||||
method:'POST', headers:{'Content-Type':'application/json'},
|
||||
body: JSON.stringify({status, note})
|
||||
});
|
||||
const wasted = document.getElementById('slotModalWasted').value;
|
||||
const actual = document.getElementById('slotModalActual').value;
|
||||
|
||||
if (status === 'done') {
|
||||
const fd = new FormData();
|
||||
fd.append('actual_g', actual);
|
||||
await fetch(`/api/slots/${id}/done`, {method:'POST', body:fd});
|
||||
} else if (status === 'failed') {
|
||||
const fd = new FormData();
|
||||
fd.append('failure_note', note);
|
||||
fd.append('wasted_g', wasted);
|
||||
await fetch(`/api/slots/${id}/fail`, {method:'POST', body:fd});
|
||||
} else {
|
||||
await fetch(`/api/slots/${id}/status`, {
|
||||
method:'POST', headers:{'Content-Type':'application/json'},
|
||||
body: JSON.stringify({status, note})
|
||||
});
|
||||
}
|
||||
bootstrap.Modal.getInstance(document.getElementById('slotModal')).hide();
|
||||
location.reload();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user