Add job edit modal on detail page
POST /jobs/{id}/edit updates: customer, material, color, priority, bracket,
printer, status, notes. Bracket field has '— none —' to detach from bracket.
Save auto-logs 'Job updated'. Edit button in page header.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@ from app.db import get_db
|
||||
from app.models.job_bracket import JobBracket
|
||||
from app.models.stl_cache import StlCache
|
||||
from app.models.job_log import JobLog
|
||||
from app.models.print_job import JobStatus, PrintJob
|
||||
from app.models.print_job import JobStatus, PrintJob # noqa: F401
|
||||
from app.models.printer import Printer, PrinterStatus
|
||||
from app.models.printer_log import PrinterLog
|
||||
from app.models.printer_type import PrinterType
|
||||
@@ -227,9 +227,45 @@ def job_detail(job_id: int, request: Request, db: Session = Depends(get_db)):
|
||||
"active": "jobs",
|
||||
"job": job,
|
||||
"stl": stl,
|
||||
"printers": db.query(Printer).order_by(Printer.name).all(),
|
||||
"brackets": db.query(JobBracket).order_by(JobBracket.name).all(),
|
||||
"materials": available_materials() or MATERIALS,
|
||||
"colors": available_colors() or COLORS,
|
||||
"all_statuses": [s.value for s in JobStatus],
|
||||
})
|
||||
|
||||
|
||||
@router.post("/jobs/{job_id}/edit")
|
||||
def edit_job(
|
||||
job_id: int,
|
||||
customer: str = Form(""),
|
||||
job_material: str = Form(""),
|
||||
job_color: str = Form(""),
|
||||
priority: str = Form(""),
|
||||
bracket_id: str = Form(""),
|
||||
printer_id: str = Form(""),
|
||||
status: str = Form(""),
|
||||
notes: str = Form(""),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
from fastapi import HTTPException
|
||||
job = db.query(PrintJob).filter(PrintJob.id == job_id).first()
|
||||
if not job:
|
||||
raise HTTPException(status_code=404, detail="Job not found")
|
||||
job.customer = customer.strip() or None
|
||||
job.job_material = job_material or None
|
||||
job.job_color = job_color or None
|
||||
job.priority = bool(priority)
|
||||
job.bracket_id = int(bracket_id) if bracket_id else None
|
||||
job.printer_id = int(printer_id) if printer_id else None
|
||||
job.notes = notes.strip() or None
|
||||
if status:
|
||||
job.status = JobStatus(status)
|
||||
db.add(JobLog(job_id=job.id, message=f"Job updated"))
|
||||
db.commit()
|
||||
return RedirectResponse(url=f"/jobs/{job_id}", status_code=HTTP_303_SEE_OTHER)
|
||||
|
||||
|
||||
@router.post("/jobs/{job_id}/analyse")
|
||||
def analyse_job_file(job_id: int, db: Session = Depends(get_db)):
|
||||
from fastapi import HTTPException
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<a href="{{ '/brackets/' ~ job.bracket.id if job.bracket else '/jobs' }}" class="text-secondary text-decoration-none">
|
||||
<i class="bi bi-arrow-left"></i>
|
||||
</a>
|
||||
<div class="flex-grow-1">
|
||||
<div class="flex-grow-1 me-3">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<h5 class="text-white mb-0">{{ job.file_name or job.name }}</h5>
|
||||
{% set s = job.status.value %}
|
||||
@@ -20,6 +20,9 @@
|
||||
</div>
|
||||
<div class="text-secondary small font-monospace mt-1">{{ job.job_uuid or '—' }}</div>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#editJobModal">
|
||||
<i class="bi bi-pencil me-1"></i>Edit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
@@ -135,6 +138,99 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# ── Edit Job Modal ── #}
|
||||
<div class="modal fade" id="editJobModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content bg-dark border-secondary">
|
||||
<form method="POST" action="/jobs/{{ job.id }}/edit">
|
||||
<div class="modal-header border-secondary">
|
||||
<h6 class="modal-title text-white">Edit Job</h6>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body d-flex flex-column gap-3">
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary small">Customer</label>
|
||||
<input type="text" name="customer" value="{{ job.customer or '' }}"
|
||||
class="form-control bg-dark border-secondary text-white">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary small">Status</label>
|
||||
<select name="status" class="form-select bg-dark border-secondary text-white">
|
||||
{% for s in all_statuses %}
|
||||
<option value="{{ s }}" {% if job.status.value == s %}selected{% endif %}>{{ s }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary small">Material</label>
|
||||
<select name="job_material" class="form-select bg-dark border-secondary text-white">
|
||||
<option value="">— none —</option>
|
||||
{% for m in materials %}
|
||||
<option value="{{ m }}" {% if job.job_material == m %}selected{% endif %}>{{ m }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary small">Color</label>
|
||||
<select name="job_color" class="form-select bg-dark border-secondary text-white">
|
||||
<option value="">— none —</option>
|
||||
{% for c in colors %}
|
||||
<option value="{{ c }}" {% if job.job_color == c %}selected{% endif %}>{{ c }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary small">Printer</label>
|
||||
<select name="printer_id" class="form-select bg-dark border-secondary text-white">
|
||||
<option value="">— unassigned —</option>
|
||||
{% for p in printers %}
|
||||
<option value="{{ p.id }}" {% if job.printer_id == p.id %}selected{% endif %}>{{ p.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label text-secondary small">Bracket</label>
|
||||
<select name="bracket_id" class="form-select bg-dark border-secondary text-white">
|
||||
<option value="">— none —</option>
|
||||
{% for b in brackets %}
|
||||
<option value="{{ b.id }}" {% if job.bracket_id == b.id %}selected{% endif %}>{{ b.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" name="priority" id="editPriorityCheck"
|
||||
value="1" {% if job.priority %}checked{% endif %}>
|
||||
<label class="form-check-label text-secondary small" for="editPriorityCheck">
|
||||
<i class="bi bi-exclamation-circle text-warning me-1"></i>Priority
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label text-secondary small">Notes</label>
|
||||
<textarea name="notes" class="form-control bg-dark border-secondary text-white"
|
||||
rows="2">{{ job.notes or '' }}</textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer border-secondary">
|
||||
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary btn-sm">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# ── Add log entry modal ── #}
|
||||
<div class="modal fade" id="addLogModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
|
||||
Reference in New Issue
Block a user