diff --git a/backend/routers/projects.py b/backend/routers/projects.py index 498c68c..cab2a34 100644 --- a/backend/routers/projects.py +++ b/backend/routers/projects.py @@ -404,6 +404,26 @@ def _build_combined_location_data( # Project List & Overview # ============================================================================ +@router.get("/list-json") +async def get_projects_list_json(db: Session = Depends(get_db)): + """JSON list of assignable projects (id, name, status) for pickers such as + the pending-deployment classify modal. Excludes deleted/archived/completed, + matching the default /list view. (The /list endpoint returns HTML cards, so + JSON consumers must use this one.)""" + projects = ( + db.query(Project) + .filter(Project.status.notin_(["deleted", "archived", "completed"])) + .order_by(Project.name) + .all() + ) + return JSONResponse({ + "projects": [ + {"id": p.id, "name": p.name, "status": p.status} + for p in projects + ] + }) + + @router.get("/list", response_class=HTMLResponse) async def get_projects_list( request: Request, diff --git a/templates/admin/pending_deployments.html b/templates/admin/pending_deployments.html index 2157f52..1d22ffd 100644 --- a/templates/admin/pending_deployments.html +++ b/templates/admin/pending_deployments.html @@ -295,9 +295,10 @@ function closeClassifyModal() { async function _loadProjects() { try { - const r = await fetch('/api/projects/list'); + // Must be the JSON endpoint — /api/projects/list returns HTML cards. + const r = await fetch('/api/projects/list-json'); const data = r.ok ? await r.json() : { projects: [] }; - // Endpoint shape varies; tolerate either { projects: [...] } or a flat array. + // Tolerate either { projects: [...] } or a flat array. _pdState.projectsCache = Array.isArray(data) ? data : (data.projects || []); } catch (e) { _pdState.projectsCache = [];