diff --git a/backend/main.py b/backend/main.py index 7accdab..48fc117 100644 --- a/backend/main.py +++ b/backend/main.py @@ -275,6 +275,22 @@ async def tools_page(request: Request): return templates.TemplateResponse("tools.html", {"request": request}) +@app.get("/deploy", response_class=HTMLResponse) +async def deploy_page(request: Request): + """Mobile-first field-capture wizard. Pick a seismograph, snap a + photo of the install, optionally add a memo — drop into the pending + hopper for later classification.""" + return templates.TemplateResponse("deploy.html", {"request": request}) + + +@app.get("/tools/pending-deployments", response_class=HTMLResponse) +async def pending_deployments_page(request: Request): + """List of field captures awaiting classification, plus filters for + historical assigned / cancelled rows. Operators promote a capture + into a real UnitAssignment from here.""" + return templates.TemplateResponse("admin/pending_deployments.html", {"request": request}) + + @app.get("/modems", response_class=HTMLResponse) async def modems_page(request: Request): """Field modems management dashboard""" diff --git a/backend/routers/pending_deployments.py b/backend/routers/pending_deployments.py index 06ae3c8..06cf2e7 100644 --- a/backend/routers/pending_deployments.py +++ b/backend/routers/pending_deployments.py @@ -67,6 +67,50 @@ def _record_history( )) +@router.get("/seismograph-picker") +def seismograph_picker( + q: str = "", + limit: int = 20, + db: Session = Depends(get_db), +): + """JSON list of seismograph units for the /deploy mobile picker. + + Filters out retired units. Sorts by recency of pending captures + first, then alphabetically — so units the operator is actively + deploying with surface at the top. + """ + q_clean = (q or "").strip() + qb = db.query(RosterUnit).filter( + RosterUnit.device_type == "seismograph", + RosterUnit.retired == False, # noqa: E712 + ) + if q_clean: + qb = qb.filter( + (RosterUnit.id.ilike(f"%{q_clean}%")) + | (RosterUnit.note.ilike(f"%{q_clean}%")) + ) + units = qb.order_by(RosterUnit.id).limit(limit).all() + + # Annotate with "has an awaiting pending deployment" so the picker + # can de-emphasize / warn on units that are already mid-deploy. + pending_unit_ids = { + r[0] for r in db.query(PendingDeployment.unit_id) + .filter_by(status="awaiting").distinct().all() + } + + return { + "units": [ + { + "id": u.id, + "note": u.note, + "deployed": u.deployed, + "has_pending": u.id in pending_unit_ids, + } + for u in units + ], + } + + @router.post("/capture") async def capture_deployment( unit_id: str = Form(...), diff --git a/templates/admin/pending_deployments.html b/templates/admin/pending_deployments.html new file mode 100644 index 0000000..2157f52 --- /dev/null +++ b/templates/admin/pending_deployments.html @@ -0,0 +1,462 @@ +{% extends "base.html" %} + +{% block title %}Pending Deployments - Seismo Fleet Manager{% endblock %} + +{% block content %} +
+ ← Back to Tools +

Pending Deployments

+

+ Captures from the field waiting to be classified. + Capture a new one → +

+
+ + +
+ + + +
+ +
+
Loading…
+
+ + + + + +{% endblock %} diff --git a/templates/dashboard.html b/templates/dashboard.html index 8b8e22c..c7f8b90 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -21,6 +21,53 @@ + + + + +
+
+

Field Deploy

+

+ Capture an install while you're still on site. Project + location can be picked later at a desk. +

+
+ + +
+
+ 1 + Unit +
+
+
+ 2 + Photo +
+
+
+ 3 + Confirm +
+
+ + +
+ +
+
+ + + + + + + + + +
+ + +{% endblock %} diff --git a/templates/tools.html b/templates/tools.html index 91c8613..d9649f6 100644 --- a/templates/tools.html +++ b/templates/tools.html @@ -14,6 +14,43 @@
+ + +
+
+ + + + +
+
+

Field Deploy

+

+ On site? Pick a unit, snap an install photo, leave. GPS is auto-captured. Classify the project/location later from a desk. +

+
+
+
+ + + +
+
+ + + +
+
+

Pending Deployments

+

+ Field captures waiting to be classified into a project + location. +

+
+
+
+