diff --git a/bincio/serve/server.py b/bincio/serve/server.py index e5a3812..d9a8e7d 100644 --- a/bincio/serve/server.py +++ b/bincio/serve/server.py @@ -2750,7 +2750,7 @@ async def list_ideas( idea["vote_count"] = len(votes) idea["my_vote"] = user.handle in votes ideas.append(idea) - ideas.sort(key=lambda x: (-x["vote_count"], -x["created_at"])) + ideas.sort(key=lambda x: (x.get("status") == "done", -x["vote_count"], -x["created_at"])) return JSONResponse({"ideas": ideas}) @@ -2806,6 +2806,28 @@ async def toggle_idea_vote( return JSONResponse({"voted": voted, "votes": len(votes)}) +@app.post("/api/ideas/{idea_id}/status") +async def toggle_idea_status( + idea_id: str, + bincio_session: Optional[str] = Cookie(default=None), +) -> JSONResponse: + user = _require_user(bincio_session) + if not user.is_admin: + raise HTTPException(403, "Forbidden") + dd = _get_data_dir() + path = _ideas_dir(dd) / f"{idea_id}.json" + if not path.exists(): + raise HTTPException(404, "Not found") + with open(path, "r+", encoding="utf-8") as f: + _fcntl.flock(f, _fcntl.LOCK_EX) + idea = json.load(f) + idea["status"] = "open" if idea.get("status") == "done" else "done" + f.seek(0) + f.truncate() + json.dump(idea, f, ensure_ascii=False, indent=2) + return JSONResponse({"status": idea["status"]}) + + @app.delete("/api/ideas/{idea_id}") async def delete_idea( idea_id: str, diff --git a/site/src/components/IdeasPage.svelte b/site/src/components/IdeasPage.svelte index c0f95d7..9807638 100644 --- a/site/src/components/IdeasPage.svelte +++ b/site/src/components/IdeasPage.svelte @@ -10,6 +10,7 @@ votes: string[]; vote_count: number; my_vote: boolean; + status?: string; } let ideas: Idea[] = []; @@ -108,6 +109,22 @@ } } + async function toggleStatus(idea: Idea) { + const r = await fetch(`/api/ideas/${idea.id}/status`, { + method: 'POST', + credentials: 'include', + }); + if (r.ok) { + const d = await r.json(); + idea.status = d.status; + ideas = [...ideas].sort((a, b) => + (a.status === 'done' ? 1 : 0) - (b.status === 'done' ? 1 : 0) || + b.vote_count - a.vote_count || + b.created_at - a.created_at + ); + } + } + async function deleteIdea(idea: Idea) { if (!confirm(`Delete "${idea.title}"?`)) return; const r = await fetch(`/api/ideas/${idea.id}`, { @@ -170,7 +187,11 @@ {:else}