diff --git a/server.py b/server.py index fe2abc5..385dc30 100644 --- a/server.py +++ b/server.py @@ -11,6 +11,7 @@ from fastapi.staticfiles import StaticFiles from pydantic import BaseModel DB_PATH = os.environ.get("DB_PATH", "/srv/form-backend/submissions.db") +STATUSES = ["received", "confirmed", "hardware ready", "installing", "completed", "cancelled"] # ---- DB Helpers ---- @@ -67,6 +68,7 @@ def init_schema(db): CREATE TABLE IF NOT EXISTS submissions ( id INTEGER PRIMARY KEY AUTOINCREMENT, form_id INTEGER NOT NULL DEFAULT 1 REFERENCES forms(id), + status TEXT NOT NULL DEFAULT 'received', created_at TEXT NOT NULL DEFAULT (datetime('now')) ); @@ -94,6 +96,13 @@ def init_schema(db): token TEXT PRIMARY KEY, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); + + CREATE TABLE IF NOT EXISTS submission_notes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + submission_id INTEGER NOT NULL REFERENCES submissions(id) ON DELETE CASCADE, + note TEXT NOT NULL, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + ); """) db.commit() @@ -326,7 +335,7 @@ def check_status(email: str = "", phone: str = ""): raise HTTPException(400, "Email and phone are required") db = get_db() rows = db.execute(""" - SELECT s.id, s.created_at + SELECT s.id, s.status, s.created_at FROM submissions s JOIN submission_data d1 ON d1.submission_id = s.id AND d1.field_label = 'Email' AND d1.field_value = ? JOIN submission_data d2 ON d2.submission_id = s.id AND d2.field_label = 'Phone' AND d2.field_value = ? @@ -336,7 +345,8 @@ def check_status(email: str = "", phone: str = ""): if not rows: raise HTTPException(404, "No submission found with that email and phone") r = rows[0] - return {"id": r["id"], "created_at": r["created_at"], "status": "received"} + status_idx = STATUSES.index(r["status"]) if r["status"] in STATUSES else 0 + return {"id": r["id"], "created_at": r["created_at"], "status": r["status"], "status_index": status_idx, "total_statuses": len(STATUSES)} # ---- Delete Submission (verify with email + phone) ---- @@ -364,6 +374,50 @@ def delete_submission(email: str = "", phone: str = ""): db.close() return {"ok": True, "deleted_id": sub_id} +# ---- Admin: Update Status ---- + +class StatusUpdate(BaseModel): + submission_id: int + status: str + +@app.post("/admin/status", status_code=200) +def update_status(body: StatusUpdate, request: Request): + check_auth(request) + if body.status not in STATUSES: + raise HTTPException(400, f"Invalid status. Must be one of: {', '.join(STATUSES)}") + db = get_db() + db.execute("UPDATE submissions SET status = ? WHERE id = ?", (body.status, body.submission_id)) + db.commit() + db.close() + return {"ok": True} + +# ---- Admin: Add Note ---- + +class NoteBody(BaseModel): + submission_id: int + note: str + +@app.post("/admin/notes", status_code=201) +def add_note(body: NoteBody, request: Request): + check_auth(request) + if not body.note.strip(): + raise HTTPException(400, "Note cannot be empty") + db = get_db() + db.execute("INSERT INTO submission_notes (submission_id, note) VALUES (?, ?)", (body.submission_id, body.note.strip())) + db.commit() + db.close() + return {"ok": True} + +# ---- Admin: Get Notes ---- + +@app.get("/admin/notes/{submission_id}") +def get_notes(submission_id: int, request: Request): + check_auth(request) + db = get_db() + rows = db.execute("SELECT id, note, created_at FROM submission_notes WHERE submission_id = ? ORDER BY id DESC", (submission_id,)).fetchall() + db.close() + return [dict(r) for r in rows] + # ---- Admin Submissions ---- @app.get("/admin/submissions") @@ -399,7 +453,7 @@ def admin_submissions( ).fetchone() result.append({ "id": r["id"], "form_id": r["form_id"], "form_name": r["form_name"], - "created_at": r["created_at"], + "created_at": r["created_at"], "status": r["status"], "data": [dict(d) for d in data], "tags": [t["tag"] for t in tags], "reply": dict(reply) if reply else None