Ideas: add reopen button when awaiting; add /reopen endpoint
This commit is contained in:
@@ -126,6 +126,28 @@ async def toggle_idea_status(
|
|||||||
return JSONResponse({"status": idea["status"]})
|
return JSONResponse({"status": idea["status"]})
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/api/ideas/{idea_id}/reopen")
|
||||||
|
async def reopen_idea(
|
||||||
|
idea_id: str,
|
||||||
|
bincio_session: Optional[str] = Cookie(default=None),
|
||||||
|
) -> JSONResponse:
|
||||||
|
user = deps._require_user(bincio_session)
|
||||||
|
if not user.is_admin:
|
||||||
|
raise HTTPException(403, "Forbidden")
|
||||||
|
dd = deps._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"
|
||||||
|
f.seek(0)
|
||||||
|
f.truncate()
|
||||||
|
json.dump(idea, f, ensure_ascii=False, indent=2)
|
||||||
|
return JSONResponse({"status": "open"})
|
||||||
|
|
||||||
|
|
||||||
@router.post("/api/ideas/{idea_id}/decline")
|
@router.post("/api/ideas/{idea_id}/decline")
|
||||||
async def decline_idea(
|
async def decline_idea(
|
||||||
idea_id: str,
|
idea_id: str,
|
||||||
|
|||||||
@@ -184,6 +184,21 @@
|
|||||||
return s === 'awaiting' ? 0 : s === 'done' ? 2 : s === 'declined' ? 3 : 1;
|
return s === 'awaiting' ? 0 : s === 'done' ? 2 : s === 'declined' ? 3 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function reopenIdea(idea: Idea) {
|
||||||
|
const r = await fetch(`/api/ideas/${idea.id}/reopen`, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
});
|
||||||
|
if (r.ok) {
|
||||||
|
idea.status = 'open';
|
||||||
|
ideas = [...ideas].sort((a, b) =>
|
||||||
|
statusOrder(a.status) - statusOrder(b.status) ||
|
||||||
|
b.vote_count - a.vote_count ||
|
||||||
|
b.created_at - a.created_at
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function declineIdea(idea: Idea) {
|
async function declineIdea(idea: Idea) {
|
||||||
const r = await fetch(`/api/ideas/${idea.id}/decline`, {
|
const r = await fetch(`/api/ideas/${idea.id}/decline`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -408,6 +423,14 @@
|
|||||||
style="color: {btnColor}; border: 1px solid {borderColor}"
|
style="color: {btnColor}; border: 1px solid {borderColor}"
|
||||||
title="Cycle status"
|
title="Cycle status"
|
||||||
>{nextLabel}</button>
|
>{nextLabel}</button>
|
||||||
|
{#if awaiting}
|
||||||
|
<button
|
||||||
|
on:click={() => reopenIdea(idea)}
|
||||||
|
class="text-xs px-1.5 py-0.5 rounded transition-colors"
|
||||||
|
style="color: var(--text-5); border: 1px solid var(--border-sub)"
|
||||||
|
title="Back to todo"
|
||||||
|
>↩</button>
|
||||||
|
{/if}
|
||||||
<button
|
<button
|
||||||
on:click={() => declineIdea(idea)}
|
on:click={() => declineIdea(idea)}
|
||||||
class="text-xs px-1.5 py-0.5 rounded transition-colors"
|
class="text-xs px-1.5 py-0.5 rounded transition-colors"
|
||||||
|
|||||||
Reference in New Issue
Block a user